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.
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
20 gcmCipherID = "aes128-gcm@openssh.com"
21 aes128cbcID = "aes128-cbc"
22 tripledescbcID = "3des-cbc"
25 // packetConn represents a transport that implements packet based
27 type packetConn interface {
28 // Encrypt and send a packet of data to the remote peer.
29 writePacket(packet []byte) error
31 // Read a packet from the connection. The read is blocking,
32 // i.e. if error is nil, then the returned byte slice is
34 readPacket() ([]byte, error)
36 // Close closes the write-side of the connection.
40 // transport is the keyingTransport that implements the SSH packet
42 type transport struct {
43 reader connectionState
44 writer connectionState
46 bufReader *bufio.Reader
47 bufWriter *bufio.Writer
53 // packetCipher represents a combination of SSH encryption/MAC
54 // protocol. A single instance should be used for one direction only.
55 type packetCipher interface {
56 // writePacket encrypts the packet and writes it to w. The
57 // contents of the packet are generally scrambled.
58 writePacket(seqnum uint32, w io.Writer, rand io.Reader, packet []byte) error
60 // readPacket reads and decrypts a packet of data. The
61 // returned packet may be overwritten by future calls of
63 readPacket(seqnum uint32, r io.Reader) ([]byte, error)
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
69 type connectionState struct {
73 pendingKeyChange chan packetCipher
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
79 func (t *transport) prepareKeyChange(algs *algorithms, kexResult *kexResult) error {
80 ciph, err := newPacketCipher(t.reader.dir, algs.r, kexResult)
84 t.reader.pendingKeyChange <- ciph
86 ciph, err = newPacketCipher(t.writer.dir, algs.w, kexResult)
90 t.writer.pendingKeyChange <- ciph
95 func (t *transport) printPacket(p []byte, write bool) {
108 log.Println(what, who, p[0])
111 // Read and decrypt next packet.
112 func (t *transport) readPacket() (p []byte, err error) {
114 p, err = t.reader.readPacket(t.bufReader)
118 if len(p) == 0 || (p[0] != msgIgnore && p[0] != msgDebug) {
123 t.printPacket(p, false)
129 func (s *connectionState) readPacket(r *bufio.Reader) ([]byte, error) {
130 packet, err := s.packetCipher.readPacket(s.seqNum, r)
132 if err == nil && len(packet) == 0 {
133 err = errors.New("ssh: zero length packet")
140 case cipher := <-s.pendingKeyChange:
141 s.packetCipher = cipher
143 return nil, errors.New("ssh: got bogus newkeys message")
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
152 var msg disconnectMsg
153 if err := Unmarshal(packet, &msg); err != nil {
160 // The packet may point to an internal buffer, so copy the
162 fresh := make([]byte, len(packet))
168 func (t *transport) writePacket(packet []byte) error {
170 t.printPacket(packet, true)
172 return t.writer.writePacket(t.bufWriter, t.rand, packet)
175 func (s *connectionState) writePacket(w *bufio.Writer, rand io.Reader, packet []byte) error {
176 changeKeys := len(packet) > 0 && packet[0] == msgNewKeys
178 err := s.packetCipher.writePacket(s.seqNum, w, rand, packet)
182 if err = w.Flush(); err != nil {
188 case cipher := <-s.pendingKeyChange:
189 s.packetCipher = cipher
191 panic("ssh: no key material for msgNewKeys")
197 func newTransport(rwc io.ReadWriteCloser, rand io.Reader, isClient bool) *transport {
199 bufReader: bufio.NewReader(rwc),
200 bufWriter: bufio.NewWriter(rwc),
202 reader: connectionState{
203 packetCipher: &streamPacketCipher{cipher: noneCipher{}},
204 pendingKeyChange: make(chan packetCipher, 1),
206 writer: connectionState{
207 packetCipher: &streamPacketCipher{cipher: noneCipher{}},
208 pendingKeyChange: make(chan packetCipher, 1),
212 t.isClient = isClient
215 t.reader.dir = serverKeys
216 t.writer.dir = clientKeys
218 t.reader.dir = clientKeys
219 t.writer.dir = serverKeys
225 type direction struct {
232 serverKeys = direction{[]byte{'B'}, []byte{'D'}, []byte{'F'}}
233 clientKeys = direction{[]byte{'A'}, []byte{'C'}, []byte{'E'}}
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).
239 func newPacketCipher(d direction, algs directionAlgorithms, kex *kexResult) (packetCipher, error) {
240 cipherMode := cipherModes[algs.Cipher]
241 macMode := macModes[algs.MAC]
243 iv := make([]byte, cipherMode.ivSize)
244 key := make([]byte, cipherMode.keySize)
245 macKey := make([]byte, macMode.keySize)
247 generateKeyMaterial(iv, d.ivTag, kex)
248 generateKeyMaterial(key, d.keyTag, kex)
249 generateKeyMaterial(macKey, d.macKeyTag, kex)
251 return cipherModes[algs.Cipher].create(key, iv, macKey, algs)
254 // generateKeyMaterial fills out with key material generated from tag, K, H
255 // and sessionId, as specified in RFC 4253, section 7.2.
256 func generateKeyMaterial(out, tag []byte, r *kexResult) {
257 var digestsSoFar []byte
265 if len(digestsSoFar) == 0 {
269 h.Write(digestsSoFar)
273 n := copy(out, digest)
276 digestsSoFar = append(digestsSoFar, digest...)
281 const packageVersion = "SSH-2.0-Go"
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.
286 func 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.
294 return nil, errors.New("ssh: junk character in version line")
297 if _, err = rw.Write(append(versionLine, '\r', '\n')); err != nil {
301 them, err = readVersion(rw)
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
308 const maxVersionStringBytes = 255
310 // Read version string as specified by RFC 4253, section 4.2.
311 func readVersion(r io.Reader) ([]byte, error) {
312 versionString := make([]byte, 0, 64)
316 for length := 0; length < maxVersionStringBytes; length++ {
317 _, err := io.ReadFull(r, buf[:])
321 // The RFC says that the version should be terminated with \r\n
322 // but several SSH servers actually only send a \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]
335 // non ASCII chars are disallowed, but we are lenient,
336 // since Go doesn't use null-terminated strings.
338 // The RFC allows a comment after a space, however,
339 // all of it (version and comments) goes into the
341 versionString = append(versionString, buf[0])
345 return nil, errors.New("ssh: overflow reading version string")
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]
352 return versionString, nil