You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

333 lines
8.7 KiB

  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. tripledescbcID = "3des-cbc"
  14. )
  15. // packetConn represents a transport that implements packet based
  16. // operations.
  17. type packetConn interface {
  18. // Encrypt and send a packet of data to the remote peer.
  19. writePacket(packet []byte) error
  20. // Read a packet from the connection
  21. readPacket() ([]byte, error)
  22. // Close closes the write-side of the connection.
  23. Close() error
  24. }
  25. // transport is the keyingTransport that implements the SSH packet
  26. // protocol.
  27. type transport struct {
  28. reader connectionState
  29. writer connectionState
  30. bufReader *bufio.Reader
  31. bufWriter *bufio.Writer
  32. rand io.Reader
  33. io.Closer
  34. }
  35. // packetCipher represents a combination of SSH encryption/MAC
  36. // protocol. A single instance should be used for one direction only.
  37. type packetCipher interface {
  38. // writePacket encrypts the packet and writes it to w. The
  39. // contents of the packet are generally scrambled.
  40. writePacket(seqnum uint32, w io.Writer, rand io.Reader, packet []byte) error
  41. // readPacket reads and decrypts a packet of data. The
  42. // returned packet may be overwritten by future calls of
  43. // readPacket.
  44. readPacket(seqnum uint32, r io.Reader) ([]byte, error)
  45. }
  46. // connectionState represents one side (read or write) of the
  47. // connection. This is necessary because each direction has its own
  48. // keys, and can even have its own algorithms
  49. type connectionState struct {
  50. packetCipher
  51. seqNum uint32
  52. dir direction
  53. pendingKeyChange chan packetCipher
  54. }
  55. // prepareKeyChange sets up key material for a keychange. The key changes in
  56. // both directions are triggered by reading and writing a msgNewKey packet
  57. // respectively.
  58. func (t *transport) prepareKeyChange(algs *algorithms, kexResult *kexResult) error {
  59. if ciph, err := newPacketCipher(t.reader.dir, algs.r, kexResult); err != nil {
  60. return err
  61. } else {
  62. t.reader.pendingKeyChange <- ciph
  63. }
  64. if ciph, err := newPacketCipher(t.writer.dir, algs.w, kexResult); err != nil {
  65. return err
  66. } else {
  67. t.writer.pendingKeyChange <- ciph
  68. }
  69. return nil
  70. }
  71. // Read and decrypt next packet.
  72. func (t *transport) readPacket() ([]byte, error) {
  73. return t.reader.readPacket(t.bufReader)
  74. }
  75. func (s *connectionState) readPacket(r *bufio.Reader) ([]byte, error) {
  76. packet, err := s.packetCipher.readPacket(s.seqNum, r)
  77. s.seqNum++
  78. if err == nil && len(packet) == 0 {
  79. err = errors.New("ssh: zero length packet")
  80. }
  81. if len(packet) > 0 {
  82. switch packet[0] {
  83. case msgNewKeys:
  84. select {
  85. case cipher := <-s.pendingKeyChange:
  86. s.packetCipher = cipher
  87. default:
  88. return nil, errors.New("ssh: got bogus newkeys message.")
  89. }
  90. case msgDisconnect:
  91. // Transform a disconnect message into an
  92. // error. Since this is lowest level at which
  93. // we interpret message types, doing it here
  94. // ensures that we don't have to handle it
  95. // elsewhere.
  96. var msg disconnectMsg
  97. if err := Unmarshal(packet, &msg); err != nil {
  98. return nil, err
  99. }
  100. return nil, &msg
  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. if algs.Cipher == tripledescbcID {
  189. return newTripleDESCBCCipher(iv, key, macKey, algs)
  190. }
  191. c := &streamPacketCipher{
  192. mac: macModes[algs.MAC].new(macKey),
  193. }
  194. c.macResult = make([]byte, c.mac.Size())
  195. var err error
  196. c.cipher, err = cipherModes[algs.Cipher].createStream(key, iv)
  197. if err != nil {
  198. return nil, err
  199. }
  200. return c, nil
  201. }
  202. // generateKeyMaterial fills out with key material generated from tag, K, H
  203. // and sessionId, as specified in RFC 4253, section 7.2.
  204. func generateKeyMaterial(out, tag []byte, r *kexResult) {
  205. var digestsSoFar []byte
  206. h := r.Hash.New()
  207. for len(out) > 0 {
  208. h.Reset()
  209. h.Write(r.K)
  210. h.Write(r.H)
  211. if len(digestsSoFar) == 0 {
  212. h.Write(tag)
  213. h.Write(r.SessionID)
  214. } else {
  215. h.Write(digestsSoFar)
  216. }
  217. digest := h.Sum(nil)
  218. n := copy(out, digest)
  219. out = out[n:]
  220. if len(out) > 0 {
  221. digestsSoFar = append(digestsSoFar, digest...)
  222. }
  223. }
  224. }
  225. const packageVersion = "SSH-2.0-Go"
  226. // Sends and receives a version line. The versionLine string should
  227. // be US ASCII, start with "SSH-2.0-", and should not include a
  228. // newline. exchangeVersions returns the other side's version line.
  229. func exchangeVersions(rw io.ReadWriter, versionLine []byte) (them []byte, err error) {
  230. // Contrary to the RFC, we do not ignore lines that don't
  231. // start with "SSH-2.0-" to make the library usable with
  232. // nonconforming servers.
  233. for _, c := range versionLine {
  234. // The spec disallows non US-ASCII chars, and
  235. // specifically forbids null chars.
  236. if c < 32 {
  237. return nil, errors.New("ssh: junk character in version line")
  238. }
  239. }
  240. if _, err = rw.Write(append(versionLine, '\r', '\n')); err != nil {
  241. return
  242. }
  243. them, err = readVersion(rw)
  244. return them, err
  245. }
  246. // maxVersionStringBytes is the maximum number of bytes that we'll
  247. // accept as a version string. RFC 4253 section 4.2 limits this at 255
  248. // chars
  249. const maxVersionStringBytes = 255
  250. // Read version string as specified by RFC 4253, section 4.2.
  251. func readVersion(r io.Reader) ([]byte, error) {
  252. versionString := make([]byte, 0, 64)
  253. var ok bool
  254. var buf [1]byte
  255. for len(versionString) < maxVersionStringBytes {
  256. _, err := io.ReadFull(r, buf[:])
  257. if err != nil {
  258. return nil, err
  259. }
  260. // The RFC says that the version should be terminated with \r\n
  261. // but several SSH servers actually only send a \n.
  262. if buf[0] == '\n' {
  263. ok = true
  264. break
  265. }
  266. // non ASCII chars are disallowed, but we are lenient,
  267. // since Go doesn't use null-terminated strings.
  268. // The RFC allows a comment after a space, however,
  269. // all of it (version and comments) goes into the
  270. // session hash.
  271. versionString = append(versionString, buf[0])
  272. }
  273. if !ok {
  274. return nil, errors.New("ssh: overflow reading version string")
  275. }
  276. // There might be a '\r' on the end which we should remove.
  277. if len(versionString) > 0 && versionString[len(versionString)-1] == '\r' {
  278. versionString = versionString[:len(versionString)-1]
  279. }
  280. return versionString, nil
  281. }