transport.go 8.6 KB

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