| 
									
										
										
										
											2017-04-11 02:25:53 +03:00
										 |  |  | // Copyright 2011 The Go Authors. All rights reserved. | 
					
						
							|  |  |  | // Use of this source code is governed by a BSD-style | 
					
						
							|  |  |  | // license that can be found in the LICENSE file. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package ssh | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bufio" | 
					
						
							|  |  |  | 	"errors" | 
					
						
							|  |  |  | 	"io" | 
					
						
							|  |  |  | 	"log" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // debugTransport if set, will print packet types as they go over the | 
					
						
							|  |  |  | // wire. No message decoding is done, to minimize the impact on timing. | 
					
						
							|  |  |  | const debugTransport = false | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	gcmCipherID    = "aes128-gcm@openssh.com" | 
					
						
							|  |  |  | 	aes128cbcID    = "aes128-cbc" | 
					
						
							|  |  |  | 	tripledescbcID = "3des-cbc" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // packetConn represents a transport that implements packet based | 
					
						
							|  |  |  | // operations. | 
					
						
							|  |  |  | type packetConn interface { | 
					
						
							|  |  |  | 	// Encrypt and send a packet of data to the remote peer. | 
					
						
							|  |  |  | 	writePacket(packet []byte) error | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Read a packet from the connection. The read is blocking, | 
					
						
							|  |  |  | 	// i.e. if error is nil, then the returned byte slice is | 
					
						
							|  |  |  | 	// always non-empty. | 
					
						
							|  |  |  | 	readPacket() ([]byte, error) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Close closes the write-side of the connection. | 
					
						
							|  |  |  | 	Close() error | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // transport is the keyingTransport that implements the SSH packet | 
					
						
							|  |  |  | // protocol. | 
					
						
							|  |  |  | type transport struct { | 
					
						
							|  |  |  | 	reader connectionState | 
					
						
							|  |  |  | 	writer connectionState | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	bufReader *bufio.Reader | 
					
						
							|  |  |  | 	bufWriter *bufio.Writer | 
					
						
							|  |  |  | 	rand      io.Reader | 
					
						
							|  |  |  | 	isClient  bool | 
					
						
							|  |  |  | 	io.Closer | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // packetCipher represents a combination of SSH encryption/MAC | 
					
						
							|  |  |  | // protocol.  A single instance should be used for one direction only. | 
					
						
							|  |  |  | type packetCipher interface { | 
					
						
							|  |  |  | 	// writePacket encrypts the packet and writes it to w. The | 
					
						
							|  |  |  | 	// contents of the packet are generally scrambled. | 
					
						
							|  |  |  | 	writePacket(seqnum uint32, w io.Writer, rand io.Reader, packet []byte) error | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// readPacket reads and decrypts a packet of data. The | 
					
						
							|  |  |  | 	// returned packet may be overwritten by future calls of | 
					
						
							|  |  |  | 	// readPacket. | 
					
						
							|  |  |  | 	readPacket(seqnum uint32, r io.Reader) ([]byte, error) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // connectionState represents one side (read or write) of the | 
					
						
							|  |  |  | // connection. This is necessary because each direction has its own | 
					
						
							|  |  |  | // keys, and can even have its own algorithms | 
					
						
							|  |  |  | type connectionState struct { | 
					
						
							|  |  |  | 	packetCipher | 
					
						
							|  |  |  | 	seqNum           uint32 | 
					
						
							|  |  |  | 	dir              direction | 
					
						
							|  |  |  | 	pendingKeyChange chan packetCipher | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // prepareKeyChange sets up key material for a keychange. The key changes in | 
					
						
							|  |  |  | // both directions are triggered by reading and writing a msgNewKey packet | 
					
						
							|  |  |  | // respectively. | 
					
						
							|  |  |  | func (t *transport) prepareKeyChange(algs *algorithms, kexResult *kexResult) error { | 
					
						
							|  |  |  | 	if ciph, err := newPacketCipher(t.reader.dir, algs.r, kexResult); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		t.reader.pendingKeyChange <- ciph | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if ciph, err := newPacketCipher(t.writer.dir, algs.w, kexResult); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		t.writer.pendingKeyChange <- ciph | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (t *transport) printPacket(p []byte, write bool) { | 
					
						
							|  |  |  | 	if len(p) == 0 { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	who := "server" | 
					
						
							|  |  |  | 	if t.isClient { | 
					
						
							|  |  |  | 		who = "client" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	what := "read" | 
					
						
							|  |  |  | 	if write { | 
					
						
							|  |  |  | 		what = "write" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	log.Println(what, who, p[0]) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Read and decrypt next packet. | 
					
						
							|  |  |  | func (t *transport) readPacket() (p []byte, err error) { | 
					
						
							|  |  |  | 	for { | 
					
						
							|  |  |  | 		p, err = t.reader.readPacket(t.bufReader) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			break | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if len(p) == 0 || (p[0] != msgIgnore && p[0] != msgDebug) { | 
					
						
							|  |  |  | 			break | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if debugTransport { | 
					
						
							|  |  |  | 		t.printPacket(p, false) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return p, err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (s *connectionState) readPacket(r *bufio.Reader) ([]byte, error) { | 
					
						
							|  |  |  | 	packet, err := s.packetCipher.readPacket(s.seqNum, r) | 
					
						
							|  |  |  | 	s.seqNum++ | 
					
						
							|  |  |  | 	if err == nil && len(packet) == 0 { | 
					
						
							|  |  |  | 		err = errors.New("ssh: zero length packet") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if len(packet) > 0 { | 
					
						
							|  |  |  | 		switch packet[0] { | 
					
						
							|  |  |  | 		case msgNewKeys: | 
					
						
							|  |  |  | 			select { | 
					
						
							|  |  |  | 			case cipher := <-s.pendingKeyChange: | 
					
						
							|  |  |  | 				s.packetCipher = cipher | 
					
						
							|  |  |  | 			default: | 
					
						
							|  |  |  | 				return nil, errors.New("ssh: got bogus newkeys message.") | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		case msgDisconnect: | 
					
						
							|  |  |  | 			// Transform a disconnect message into an | 
					
						
							|  |  |  | 			// error. Since this is lowest level at which | 
					
						
							|  |  |  | 			// we interpret message types, doing it here | 
					
						
							|  |  |  | 			// ensures that we don't have to handle it | 
					
						
							|  |  |  | 			// elsewhere. | 
					
						
							|  |  |  | 			var msg disconnectMsg | 
					
						
							|  |  |  | 			if err := Unmarshal(packet, &msg); err != nil { | 
					
						
							|  |  |  | 				return nil, err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return nil, &msg | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// The packet may point to an internal buffer, so copy the | 
					
						
							|  |  |  | 	// packet out here. | 
					
						
							|  |  |  | 	fresh := make([]byte, len(packet)) | 
					
						
							|  |  |  | 	copy(fresh, packet) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return fresh, err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (t *transport) writePacket(packet []byte) error { | 
					
						
							|  |  |  | 	if debugTransport { | 
					
						
							|  |  |  | 		t.printPacket(packet, true) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return t.writer.writePacket(t.bufWriter, t.rand, packet) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (s *connectionState) writePacket(w *bufio.Writer, rand io.Reader, packet []byte) error { | 
					
						
							|  |  |  | 	changeKeys := len(packet) > 0 && packet[0] == msgNewKeys | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err := s.packetCipher.writePacket(s.seqNum, w, rand, packet) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err = w.Flush(); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	s.seqNum++ | 
					
						
							|  |  |  | 	if changeKeys { | 
					
						
							|  |  |  | 		select { | 
					
						
							|  |  |  | 		case cipher := <-s.pendingKeyChange: | 
					
						
							|  |  |  | 			s.packetCipher = cipher | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			panic("ssh: no key material for msgNewKeys") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func newTransport(rwc io.ReadWriteCloser, rand io.Reader, isClient bool) *transport { | 
					
						
							|  |  |  | 	t := &transport{ | 
					
						
							|  |  |  | 		bufReader: bufio.NewReader(rwc), | 
					
						
							|  |  |  | 		bufWriter: bufio.NewWriter(rwc), | 
					
						
							|  |  |  | 		rand:      rand, | 
					
						
							|  |  |  | 		reader: connectionState{ | 
					
						
							|  |  |  | 			packetCipher:     &streamPacketCipher{cipher: noneCipher{}}, | 
					
						
							|  |  |  | 			pendingKeyChange: make(chan packetCipher, 1), | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		writer: connectionState{ | 
					
						
							|  |  |  | 			packetCipher:     &streamPacketCipher{cipher: noneCipher{}}, | 
					
						
							|  |  |  | 			pendingKeyChange: make(chan packetCipher, 1), | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		Closer: rwc, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	t.isClient = isClient | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if isClient { | 
					
						
							|  |  |  | 		t.reader.dir = serverKeys | 
					
						
							|  |  |  | 		t.writer.dir = clientKeys | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		t.reader.dir = clientKeys | 
					
						
							|  |  |  | 		t.writer.dir = serverKeys | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return t | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type direction struct { | 
					
						
							|  |  |  | 	ivTag     []byte | 
					
						
							|  |  |  | 	keyTag    []byte | 
					
						
							|  |  |  | 	macKeyTag []byte | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var ( | 
					
						
							|  |  |  | 	serverKeys = direction{[]byte{'B'}, []byte{'D'}, []byte{'F'}} | 
					
						
							|  |  |  | 	clientKeys = direction{[]byte{'A'}, []byte{'C'}, []byte{'E'}} | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // generateKeys generates key material for IV, MAC and encryption. | 
					
						
							|  |  |  | func generateKeys(d direction, algs directionAlgorithms, kex *kexResult) (iv, key, macKey []byte) { | 
					
						
							|  |  |  | 	cipherMode := cipherModes[algs.Cipher] | 
					
						
							|  |  |  | 	macMode := macModes[algs.MAC] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	iv = make([]byte, cipherMode.ivSize) | 
					
						
							|  |  |  | 	key = make([]byte, cipherMode.keySize) | 
					
						
							|  |  |  | 	macKey = make([]byte, macMode.keySize) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	generateKeyMaterial(iv, d.ivTag, kex) | 
					
						
							|  |  |  | 	generateKeyMaterial(key, d.keyTag, kex) | 
					
						
							|  |  |  | 	generateKeyMaterial(macKey, d.macKeyTag, kex) | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // setupKeys sets the cipher and MAC keys from kex.K, kex.H and sessionId, as | 
					
						
							|  |  |  | // described in RFC 4253, section 6.4. direction should either be serverKeys | 
					
						
							|  |  |  | // (to setup server->client keys) or clientKeys (for client->server keys). | 
					
						
							|  |  |  | func newPacketCipher(d direction, algs directionAlgorithms, kex *kexResult) (packetCipher, error) { | 
					
						
							|  |  |  | 	iv, key, macKey := generateKeys(d, algs, kex) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if algs.Cipher == gcmCipherID { | 
					
						
							| 
									
										
										
										
											2017-11-12 12:24:42 -08:00
										 |  |  | 		return newGCMCipher(iv, key) | 
					
						
							| 
									
										
										
										
											2017-04-11 02:25:53 +03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if algs.Cipher == aes128cbcID { | 
					
						
							|  |  |  | 		return newAESCBCCipher(iv, key, macKey, algs) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if algs.Cipher == tripledescbcID { | 
					
						
							|  |  |  | 		return newTripleDESCBCCipher(iv, key, macKey, algs) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	c := &streamPacketCipher{ | 
					
						
							|  |  |  | 		mac: macModes[algs.MAC].new(macKey), | 
					
						
							|  |  |  | 		etm: macModes[algs.MAC].etm, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	c.macResult = make([]byte, c.mac.Size()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var err error | 
					
						
							|  |  |  | 	c.cipher, err = cipherModes[algs.Cipher].createStream(key, iv) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return c, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // generateKeyMaterial fills out with key material generated from tag, K, H | 
					
						
							|  |  |  | // and sessionId, as specified in RFC 4253, section 7.2. | 
					
						
							|  |  |  | func generateKeyMaterial(out, tag []byte, r *kexResult) { | 
					
						
							|  |  |  | 	var digestsSoFar []byte | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	h := r.Hash.New() | 
					
						
							|  |  |  | 	for len(out) > 0 { | 
					
						
							|  |  |  | 		h.Reset() | 
					
						
							|  |  |  | 		h.Write(r.K) | 
					
						
							|  |  |  | 		h.Write(r.H) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if len(digestsSoFar) == 0 { | 
					
						
							|  |  |  | 			h.Write(tag) | 
					
						
							|  |  |  | 			h.Write(r.SessionID) | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			h.Write(digestsSoFar) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		digest := h.Sum(nil) | 
					
						
							|  |  |  | 		n := copy(out, digest) | 
					
						
							|  |  |  | 		out = out[n:] | 
					
						
							|  |  |  | 		if len(out) > 0 { | 
					
						
							|  |  |  | 			digestsSoFar = append(digestsSoFar, digest...) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const packageVersion = "SSH-2.0-Go" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Sends and receives a version line.  The versionLine string should | 
					
						
							|  |  |  | // be US ASCII, start with "SSH-2.0-", and should not include a | 
					
						
							|  |  |  | // newline. exchangeVersions returns the other side's version line. | 
					
						
							|  |  |  | func exchangeVersions(rw io.ReadWriter, versionLine []byte) (them []byte, err error) { | 
					
						
							|  |  |  | 	// Contrary to the RFC, we do not ignore lines that don't | 
					
						
							|  |  |  | 	// start with "SSH-2.0-" to make the library usable with | 
					
						
							|  |  |  | 	// nonconforming servers. | 
					
						
							|  |  |  | 	for _, c := range versionLine { | 
					
						
							|  |  |  | 		// The spec disallows non US-ASCII chars, and | 
					
						
							|  |  |  | 		// specifically forbids null chars. | 
					
						
							|  |  |  | 		if c < 32 { | 
					
						
							|  |  |  | 			return nil, errors.New("ssh: junk character in version line") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if _, err = rw.Write(append(versionLine, '\r', '\n')); err != nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	them, err = readVersion(rw) | 
					
						
							|  |  |  | 	return them, err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // maxVersionStringBytes is the maximum number of bytes that we'll | 
					
						
							|  |  |  | // accept as a version string. RFC 4253 section 4.2 limits this at 255 | 
					
						
							|  |  |  | // chars | 
					
						
							|  |  |  | const maxVersionStringBytes = 255 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Read version string as specified by RFC 4253, section 4.2. | 
					
						
							|  |  |  | func readVersion(r io.Reader) ([]byte, error) { | 
					
						
							|  |  |  | 	versionString := make([]byte, 0, 64) | 
					
						
							|  |  |  | 	var ok bool | 
					
						
							|  |  |  | 	var buf [1]byte | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for len(versionString) < maxVersionStringBytes { | 
					
						
							|  |  |  | 		_, err := io.ReadFull(r, buf[:]) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// The RFC says that the version should be terminated with \r\n | 
					
						
							|  |  |  | 		// but several SSH servers actually only send a \n. | 
					
						
							|  |  |  | 		if buf[0] == '\n' { | 
					
						
							|  |  |  | 			ok = true | 
					
						
							|  |  |  | 			break | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// non ASCII chars are disallowed, but we are lenient, | 
					
						
							|  |  |  | 		// since Go doesn't use null-terminated strings. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// The RFC allows a comment after a space, however, | 
					
						
							|  |  |  | 		// all of it (version and comments) goes into the | 
					
						
							|  |  |  | 		// session hash. | 
					
						
							|  |  |  | 		versionString = append(versionString, buf[0]) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							|  |  |  | 		return nil, errors.New("ssh: overflow reading version string") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// There might be a '\r' on the end which we should remove. | 
					
						
							|  |  |  | 	if len(versionString) > 0 && versionString[len(versionString)-1] == '\r' { | 
					
						
							|  |  |  | 		versionString = versionString[:len(versionString)-1] | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return versionString, nil | 
					
						
							|  |  |  | } |