transport.go 8.5 KB

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