net-ssh 3.2.0.rc2 → 7.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (204) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +2 -2
  3. data/.dockerignore +6 -0
  4. data/.github/config/rubocop_linter_action.yml +4 -0
  5. data/.github/workflows/ci-with-docker.yml +44 -0
  6. data/.github/workflows/ci.yml +87 -0
  7. data/.github/workflows/rubocop.yml +16 -0
  8. data/.gitignore +13 -0
  9. data/.rubocop.yml +22 -0
  10. data/.rubocop_todo.yml +1081 -0
  11. data/CHANGES.txt +228 -7
  12. data/Dockerfile +27 -0
  13. data/Dockerfile.openssl3 +17 -0
  14. data/Gemfile +13 -0
  15. data/Gemfile.noed25519 +12 -0
  16. data/ISSUE_TEMPLATE.md +30 -0
  17. data/Manifest +4 -5
  18. data/README.md +297 -0
  19. data/Rakefile +125 -74
  20. data/SECURITY.md +4 -0
  21. data/appveyor.yml +58 -0
  22. data/docker-compose.yml +23 -0
  23. data/lib/net/ssh/authentication/agent.rb +279 -18
  24. data/lib/net/ssh/authentication/certificate.rb +183 -0
  25. data/lib/net/ssh/authentication/constants.rb +17 -15
  26. data/lib/net/ssh/authentication/ed25519.rb +186 -0
  27. data/lib/net/ssh/authentication/ed25519_loader.rb +31 -0
  28. data/lib/net/ssh/authentication/key_manager.rb +86 -39
  29. data/lib/net/ssh/authentication/methods/abstract.rb +67 -48
  30. data/lib/net/ssh/authentication/methods/hostbased.rb +34 -37
  31. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +13 -13
  32. data/lib/net/ssh/authentication/methods/none.rb +16 -19
  33. data/lib/net/ssh/authentication/methods/password.rb +27 -17
  34. data/lib/net/ssh/authentication/methods/publickey.rb +96 -55
  35. data/lib/net/ssh/authentication/pageant.rb +471 -367
  36. data/lib/net/ssh/authentication/pub_key_fingerprint.rb +43 -0
  37. data/lib/net/ssh/authentication/session.rb +131 -121
  38. data/lib/net/ssh/buffer.rb +399 -300
  39. data/lib/net/ssh/buffered_io.rb +154 -150
  40. data/lib/net/ssh/config.rb +308 -185
  41. data/lib/net/ssh/connection/channel.rb +635 -613
  42. data/lib/net/ssh/connection/constants.rb +29 -29
  43. data/lib/net/ssh/connection/event_loop.rb +123 -0
  44. data/lib/net/ssh/connection/keepalive.rb +55 -51
  45. data/lib/net/ssh/connection/session.rb +620 -551
  46. data/lib/net/ssh/connection/term.rb +125 -123
  47. data/lib/net/ssh/errors.rb +101 -99
  48. data/lib/net/ssh/key_factory.rb +197 -105
  49. data/lib/net/ssh/known_hosts.rb +214 -127
  50. data/lib/net/ssh/loggable.rb +50 -49
  51. data/lib/net/ssh/packet.rb +83 -79
  52. data/lib/net/ssh/prompt.rb +50 -81
  53. data/lib/net/ssh/proxy/command.rb +105 -90
  54. data/lib/net/ssh/proxy/errors.rb +12 -10
  55. data/lib/net/ssh/proxy/http.rb +82 -79
  56. data/lib/net/ssh/proxy/https.rb +50 -0
  57. data/lib/net/ssh/proxy/jump.rb +54 -0
  58. data/lib/net/ssh/proxy/socks4.rb +2 -6
  59. data/lib/net/ssh/proxy/socks5.rb +14 -17
  60. data/lib/net/ssh/service/forward.rb +370 -317
  61. data/lib/net/ssh/test/channel.rb +145 -136
  62. data/lib/net/ssh/test/extensions.rb +131 -110
  63. data/lib/net/ssh/test/kex.rb +34 -32
  64. data/lib/net/ssh/test/local_packet.rb +46 -44
  65. data/lib/net/ssh/test/packet.rb +89 -70
  66. data/lib/net/ssh/test/remote_packet.rb +32 -30
  67. data/lib/net/ssh/test/script.rb +156 -142
  68. data/lib/net/ssh/test/socket.rb +49 -48
  69. data/lib/net/ssh/test.rb +82 -77
  70. data/lib/net/ssh/transport/algorithms.rb +441 -360
  71. data/lib/net/ssh/transport/cipher_factory.rb +96 -98
  72. data/lib/net/ssh/transport/constants.rb +32 -24
  73. data/lib/net/ssh/transport/ctr.rb +42 -22
  74. data/lib/net/ssh/transport/hmac/abstract.rb +81 -63
  75. data/lib/net/ssh/transport/hmac/md5.rb +0 -2
  76. data/lib/net/ssh/transport/hmac/md5_96.rb +0 -2
  77. data/lib/net/ssh/transport/hmac/none.rb +0 -2
  78. data/lib/net/ssh/transport/hmac/ripemd160.rb +0 -2
  79. data/lib/net/ssh/transport/hmac/sha1.rb +0 -2
  80. data/lib/net/ssh/transport/hmac/sha1_96.rb +0 -2
  81. data/lib/net/ssh/transport/hmac/sha2_256.rb +7 -11
  82. data/lib/net/ssh/transport/hmac/sha2_256_96.rb +4 -8
  83. data/lib/net/ssh/transport/hmac/sha2_256_etm.rb +12 -0
  84. data/lib/net/ssh/transport/hmac/sha2_512.rb +6 -9
  85. data/lib/net/ssh/transport/hmac/sha2_512_96.rb +4 -8
  86. data/lib/net/ssh/transport/hmac/sha2_512_etm.rb +12 -0
  87. data/lib/net/ssh/transport/hmac.rb +14 -12
  88. data/lib/net/ssh/transport/identity_cipher.rb +54 -52
  89. data/lib/net/ssh/transport/kex/abstract.rb +130 -0
  90. data/lib/net/ssh/transport/kex/abstract5656.rb +72 -0
  91. data/lib/net/ssh/transport/kex/curve25519_sha256.rb +39 -0
  92. data/lib/net/ssh/transport/kex/curve25519_sha256_loader.rb +30 -0
  93. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +33 -40
  94. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha256.rb +11 -0
  95. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +119 -213
  96. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +53 -61
  97. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha256.rb +5 -9
  98. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +36 -90
  99. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +18 -10
  100. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +18 -10
  101. data/lib/net/ssh/transport/kex.rb +15 -12
  102. data/lib/net/ssh/transport/key_expander.rb +24 -20
  103. data/lib/net/ssh/transport/openssl.rb +161 -124
  104. data/lib/net/ssh/transport/packet_stream.rb +225 -185
  105. data/lib/net/ssh/transport/server_version.rb +55 -56
  106. data/lib/net/ssh/transport/session.rb +306 -255
  107. data/lib/net/ssh/transport/state.rb +178 -176
  108. data/lib/net/ssh/verifiers/accept_new.rb +33 -0
  109. data/lib/net/ssh/verifiers/accept_new_or_local_tunnel.rb +33 -0
  110. data/lib/net/ssh/verifiers/always.rb +58 -0
  111. data/lib/net/ssh/verifiers/never.rb +19 -0
  112. data/lib/net/ssh/version.rb +55 -53
  113. data/lib/net/ssh.rb +110 -47
  114. data/net-ssh-public_cert.pem +18 -18
  115. data/net-ssh.gemspec +36 -205
  116. data/support/ssh_tunnel_bug.rb +5 -5
  117. data.tar.gz.sig +0 -0
  118. metadata +153 -118
  119. metadata.gz.sig +0 -0
  120. data/.travis.yml +0 -18
  121. data/README.rdoc +0 -182
  122. data/lib/net/ssh/authentication/agent/java_pageant.rb +0 -85
  123. data/lib/net/ssh/authentication/agent/socket.rb +0 -178
  124. data/lib/net/ssh/ruby_compat.rb +0 -46
  125. data/lib/net/ssh/verifiers/lenient.rb +0 -30
  126. data/lib/net/ssh/verifiers/null.rb +0 -12
  127. data/lib/net/ssh/verifiers/secure.rb +0 -52
  128. data/lib/net/ssh/verifiers/strict.rb +0 -24
  129. data/setup.rb +0 -1585
  130. data/support/arcfour_check.rb +0 -20
  131. data/test/README.txt +0 -18
  132. data/test/authentication/methods/common.rb +0 -28
  133. data/test/authentication/methods/test_abstract.rb +0 -51
  134. data/test/authentication/methods/test_hostbased.rb +0 -114
  135. data/test/authentication/methods/test_keyboard_interactive.rb +0 -121
  136. data/test/authentication/methods/test_none.rb +0 -41
  137. data/test/authentication/methods/test_password.rb +0 -95
  138. data/test/authentication/methods/test_publickey.rb +0 -148
  139. data/test/authentication/test_agent.rb +0 -232
  140. data/test/authentication/test_key_manager.rb +0 -240
  141. data/test/authentication/test_session.rb +0 -107
  142. data/test/common.rb +0 -125
  143. data/test/configs/auth_off +0 -5
  144. data/test/configs/auth_on +0 -4
  145. data/test/configs/empty +0 -0
  146. data/test/configs/eqsign +0 -3
  147. data/test/configs/exact_match +0 -8
  148. data/test/configs/host_plus +0 -10
  149. data/test/configs/multihost +0 -4
  150. data/test/configs/negative_match +0 -6
  151. data/test/configs/nohost +0 -19
  152. data/test/configs/numeric_host +0 -4
  153. data/test/configs/proxy_remote_user +0 -2
  154. data/test/configs/send_env +0 -2
  155. data/test/configs/substitutes +0 -8
  156. data/test/configs/wild_cards +0 -14
  157. data/test/connection/test_channel.rb +0 -487
  158. data/test/connection/test_session.rb +0 -564
  159. data/test/integration/README.txt +0 -17
  160. data/test/integration/Vagrantfile +0 -12
  161. data/test/integration/common.rb +0 -63
  162. data/test/integration/playbook.yml +0 -56
  163. data/test/integration/test_forward.rb +0 -637
  164. data/test/integration/test_id_rsa_keys.rb +0 -96
  165. data/test/integration/test_proxy.rb +0 -93
  166. data/test/known_hosts/github +0 -1
  167. data/test/known_hosts/github_hash +0 -1
  168. data/test/manual/test_pageant.rb +0 -37
  169. data/test/start/test_connection.rb +0 -53
  170. data/test/start/test_options.rb +0 -57
  171. data/test/start/test_transport.rb +0 -28
  172. data/test/start/test_user_nil.rb +0 -27
  173. data/test/test_all.rb +0 -12
  174. data/test/test_buffer.rb +0 -433
  175. data/test/test_buffered_io.rb +0 -63
  176. data/test/test_config.rb +0 -268
  177. data/test/test_key_factory.rb +0 -191
  178. data/test/test_known_hosts.rb +0 -66
  179. data/test/transport/hmac/test_md5.rb +0 -41
  180. data/test/transport/hmac/test_md5_96.rb +0 -27
  181. data/test/transport/hmac/test_none.rb +0 -34
  182. data/test/transport/hmac/test_ripemd160.rb +0 -36
  183. data/test/transport/hmac/test_sha1.rb +0 -36
  184. data/test/transport/hmac/test_sha1_96.rb +0 -27
  185. data/test/transport/hmac/test_sha2_256.rb +0 -37
  186. data/test/transport/hmac/test_sha2_256_96.rb +0 -27
  187. data/test/transport/hmac/test_sha2_512.rb +0 -37
  188. data/test/transport/hmac/test_sha2_512_96.rb +0 -27
  189. data/test/transport/kex/test_diffie_hellman_group14_sha1.rb +0 -13
  190. data/test/transport/kex/test_diffie_hellman_group1_sha1.rb +0 -150
  191. data/test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb +0 -96
  192. data/test/transport/kex/test_diffie_hellman_group_exchange_sha256.rb +0 -19
  193. data/test/transport/kex/test_ecdh_sha2_nistp256.rb +0 -161
  194. data/test/transport/kex/test_ecdh_sha2_nistp384.rb +0 -38
  195. data/test/transport/kex/test_ecdh_sha2_nistp521.rb +0 -38
  196. data/test/transport/test_algorithms.rb +0 -328
  197. data/test/transport/test_cipher_factory.rb +0 -443
  198. data/test/transport/test_hmac.rb +0 -34
  199. data/test/transport/test_identity_cipher.rb +0 -40
  200. data/test/transport/test_packet_stream.rb +0 -1762
  201. data/test/transport/test_server_version.rb +0 -74
  202. data/test/transport/test_session.rb +0 -331
  203. data/test/transport/test_state.rb +0 -181
  204. data/test/verifiers/test_secure.rb +0 -40
@@ -1,240 +1,280 @@
1
1
  require 'net/ssh/buffered_io'
2
2
  require 'net/ssh/errors'
3
3
  require 'net/ssh/packet'
4
- require 'net/ssh/ruby_compat'
5
4
  require 'net/ssh/transport/cipher_factory'
6
5
  require 'net/ssh/transport/hmac'
7
6
  require 'net/ssh/transport/state'
8
7
 
8
+ module Net
9
+ module SSH
10
+ module Transport
11
+ # A module that builds additional functionality onto the Net::SSH::BufferedIo
12
+ # module. It adds SSH encryption, compression, and packet validation, as
13
+ # per the SSH2 protocol. It also adds an abstraction for polling packets,
14
+ # to allow for both blocking and non-blocking reads.
15
+ module PacketStream
16
+ PROXY_COMMAND_HOST_IP = '<no hostip for proxy command>'.freeze
9
17
 
10
- module Net; module SSH; module Transport
18
+ include BufferedIo
11
19
 
12
- # A module that builds additional functionality onto the Net::SSH::BufferedIo
13
- # module. It adds SSH encryption, compression, and packet validation, as
14
- # per the SSH2 protocol. It also adds an abstraction for polling packets,
15
- # to allow for both blocking and non-blocking reads.
16
- module PacketStream
17
- PROXY_COMMAND_HOST_IP = '<no hostip for proxy command>'.freeze
18
-
19
- include BufferedIo
20
-
21
- def self.extended(object)
22
- object.__send__(:initialize_ssh)
23
- end
20
+ def self.extended(object)
21
+ object.__send__(:initialize_ssh)
22
+ end
24
23
 
25
- # The map of "hints" that can be used to modify the behavior of the packet
26
- # stream. For instance, when authentication succeeds, an "authenticated"
27
- # hint is set, which is used to determine whether or not to compress the
28
- # data when using the "delayed" compression algorithm.
29
- attr_reader :hints
30
-
31
- # The server state object, which encapsulates the algorithms used to interpret
32
- # packets coming from the server.
33
- attr_reader :server
34
-
35
- # The client state object, which encapsulates the algorithms used to build
36
- # packets to send to the server.
37
- attr_reader :client
38
-
39
- # The name of the client (local) end of the socket, as reported by the
40
- # socket.
41
- def client_name
42
- @client_name ||= begin
43
- sockaddr = getsockname
44
- begin
45
- Socket.getnameinfo(sockaddr, Socket::NI_NAMEREQD).first
46
- rescue
47
- begin
48
- Socket.getnameinfo(sockaddr).first
49
- rescue
24
+ # The map of "hints" that can be used to modify the behavior of the packet
25
+ # stream. For instance, when authentication succeeds, an "authenticated"
26
+ # hint is set, which is used to determine whether or not to compress the
27
+ # data when using the "delayed" compression algorithm.
28
+ attr_reader :hints
29
+
30
+ # The server state object, which encapsulates the algorithms used to interpret
31
+ # packets coming from the server.
32
+ attr_reader :server
33
+
34
+ # The client state object, which encapsulates the algorithms used to build
35
+ # packets to send to the server.
36
+ attr_reader :client
37
+
38
+ # The name of the client (local) end of the socket, as reported by the
39
+ # socket.
40
+ def client_name
41
+ @client_name ||= begin
42
+ sockaddr = getsockname
50
43
  begin
51
- Socket.gethostbyname(Socket.gethostname).first
52
- rescue
53
- lwarn { "the client ipaddr/name could not be determined" }
54
- "unknown"
44
+ Socket.getnameinfo(sockaddr, Socket::NI_NAMEREQD).first
45
+ rescue StandardError
46
+ begin
47
+ Socket.getnameinfo(sockaddr).first
48
+ rescue StandardError
49
+ begin
50
+ Socket.gethostbyname(Socket.gethostname).first
51
+ rescue StandardError
52
+ lwarn { "the client ipaddr/name could not be determined" }
53
+ "unknown"
54
+ end
55
+ end
55
56
  end
56
57
  end
57
58
  end
58
- end
59
- end
60
59
 
61
- # The IP address of the peer (remote) end of the socket, as reported by
62
- # the socket.
63
- def peer_ip
64
- @peer_ip ||=
65
- if respond_to?(:getpeername)
66
- addr = getpeername
67
- Socket.getnameinfo(addr, Socket::NI_NUMERICHOST | Socket::NI_NUMERICSERV).first
68
- else
69
- PROXY_COMMAND_HOST_IP
60
+ # The IP address of the peer (remote) end of the socket, as reported by
61
+ # the socket.
62
+ def peer_ip
63
+ @peer_ip ||=
64
+ if respond_to?(:getpeername)
65
+ addr = getpeername
66
+ Socket.getnameinfo(addr, Socket::NI_NUMERICHOST | Socket::NI_NUMERICSERV).first
67
+ else
68
+ PROXY_COMMAND_HOST_IP
69
+ end
70
70
  end
71
- end
72
-
73
- # Returns true if the IO is available for reading, and false otherwise.
74
- def available_for_read?
75
- result = Net::SSH::Compat.io_select([self], nil, nil, 0)
76
- result && result.first.any?
77
- end
78
-
79
- # Returns the next full packet. If the mode parameter is :nonblock (the
80
- # default), then this will return immediately, whether a packet is
81
- # available or not, and will return nil if there is no packet ready to be
82
- # returned. If the mode parameter is :block, then this method will block
83
- # until a packet is available.
84
- def next_packet(mode=:nonblock)
85
- case mode
86
- when :nonblock then
87
- if available_for_read?
88
- if fill <= 0
89
- raise Net::SSH::Disconnect, "connection closed by remote host"
90
- end
71
+
72
+ # Returns true if the IO is available for reading, and false otherwise.
73
+ def available_for_read?
74
+ result = IO.select([self], nil, nil, 0)
75
+ result && result.first.any?
91
76
  end
92
- poll_next_packet
93
77
 
94
- when :block then
95
- loop do
96
- packet = poll_next_packet
97
- return packet if packet
78
+ # Returns the next full packet. If the mode parameter is :nonblock (the
79
+ # default), then this will return immediately, whether a packet is
80
+ # available or not, and will return nil if there is no packet ready to be
81
+ # returned. If the mode parameter is :block, then this method will block
82
+ # until a packet is available or timeout seconds have passed.
83
+ def next_packet(mode = :nonblock, timeout = nil)
84
+ case mode
85
+ when :nonblock then
86
+ packet = poll_next_packet
87
+ return packet if packet
88
+
89
+ if available_for_read?
90
+ if fill <= 0
91
+ result = poll_next_packet
92
+ if result.nil?
93
+ raise Net::SSH::Disconnect, "connection closed by remote host"
94
+ else
95
+ return result
96
+ end
97
+ end
98
+ end
99
+ poll_next_packet
98
100
 
99
- loop do
100
- result = Net::SSH::Compat.io_select([self]) or next
101
- break if result.first.any?
102
- end
101
+ when :block then
102
+ loop do
103
+ packet = poll_next_packet
104
+ return packet if packet
105
+
106
+ result = IO.select([self], nil, nil, timeout)
107
+ raise Net::SSH::ConnectionTimeout, "timeout waiting for next packet" unless result
108
+ raise Net::SSH::Disconnect, "connection closed by remote host" if fill <= 0
109
+ end
103
110
 
104
- if fill <= 0
105
- raise Net::SSH::Disconnect, "connection closed by remote host"
111
+ else
112
+ raise ArgumentError, "expected :block or :nonblock, got #{mode.inspect}"
106
113
  end
107
114
  end
108
115
 
109
- else
110
- raise ArgumentError, "expected :block or :nonblock, got #{mode.inspect}"
111
- end
112
- end
116
+ # Enqueues a packet to be sent, and blocks until the entire packet is
117
+ # sent.
118
+ def send_packet(payload)
119
+ enqueue_packet(payload)
120
+ wait_for_pending_sends
121
+ end
113
122
 
114
- # Enqueues a packet to be sent, and blocks until the entire packet is
115
- # sent.
116
- def send_packet(payload)
117
- enqueue_packet(payload)
118
- wait_for_pending_sends
119
- end
123
+ # Enqueues a packet to be sent, but does not immediately send the packet.
124
+ # The given payload is pre-processed according to the algorithms specified
125
+ # in the client state (compression, cipher, and hmac).
126
+ def enqueue_packet(payload)
127
+ # try to compress the packet
128
+ payload = client.compress(payload)
120
129
 
121
- # Enqueues a packet to be sent, but does not immediately send the packet.
122
- # The given payload is pre-processed according to the algorithms specified
123
- # in the client state (compression, cipher, and hmac).
124
- def enqueue_packet(payload)
125
- # try to compress the packet
126
- payload = client.compress(payload)
130
+ # the length of the packet, minus the padding
131
+ actual_length = (client.hmac.etm ? 0 : 4) + payload.bytesize + 1
127
132
 
128
- # the length of the packet, minus the padding
129
- actual_length = 4 + payload.bytesize + 1
133
+ # compute the padding length
134
+ padding_length = client.block_size - (actual_length % client.block_size)
135
+ padding_length += client.block_size if padding_length < 4
130
136
 
131
- # compute the padding length
132
- padding_length = client.block_size - (actual_length % client.block_size)
133
- padding_length += client.block_size if padding_length < 4
137
+ # compute the packet length (sans the length field itself)
138
+ packet_length = payload.bytesize + padding_length + 1
134
139
 
135
- # compute the packet length (sans the length field itself)
136
- packet_length = payload.bytesize + padding_length + 1
140
+ if packet_length < 16
141
+ padding_length += client.block_size
142
+ packet_length = payload.bytesize + padding_length + 1
143
+ end
137
144
 
138
- if packet_length < 16
139
- padding_length += client.block_size
140
- packet_length = payload.bytesize + padding_length + 1
141
- end
145
+ padding = Array.new(padding_length) { rand(256) }.pack("C*")
142
146
 
143
- padding = Array.new(padding_length) { rand(256) }.pack("C*")
147
+ if client.hmac.etm
148
+ debug { "using encrypt-then-mac" }
144
149
 
145
- unencrypted_data = [packet_length, padding_length, payload, padding].pack("NCA*A*")
146
- mac = client.hmac.digest([client.sequence_number, unencrypted_data].pack("NA*"))
150
+ # Encrypt padding_length, payload, and padding. Take MAC
151
+ # from the unencrypted packet_lenght and the encrypted
152
+ # data.
153
+ length_data = [packet_length].pack("N")
147
154
 
148
- encrypted_data = client.update_cipher(unencrypted_data) << client.final_cipher
149
- message = encrypted_data + mac
155
+ unencrypted_data = [padding_length, payload, padding].pack("CA*A*")
150
156
 
151
- debug { "queueing packet nr #{client.sequence_number} type #{payload.getbyte(0)} len #{packet_length}" }
152
- enqueue(message)
157
+ encrypted_data = client.update_cipher(unencrypted_data) << client.final_cipher
153
158
 
154
- client.increment(packet_length)
159
+ mac_data = length_data + encrypted_data
155
160
 
156
- self
157
- end
161
+ mac = client.hmac.digest([client.sequence_number, mac_data].pack("NA*"))
158
162
 
159
- # Performs any pending cleanup necessary on the IO and its associated
160
- # state objects. (See State#cleanup).
161
- def cleanup
162
- client.cleanup
163
- server.cleanup
164
- end
163
+ message = mac_data + mac
164
+ else
165
+ unencrypted_data = [packet_length, padding_length, payload, padding].pack("NCA*A*")
165
166
 
166
- # If the IO object requires a rekey operation (as indicated by either its
167
- # client or server state objects, see State#needs_rekey?), this will
168
- # yield. Otherwise, this does nothing.
169
- def if_needs_rekey?
170
- if client.needs_rekey? || server.needs_rekey?
171
- yield
172
- client.reset! if client.needs_rekey?
173
- server.reset! if server.needs_rekey?
174
- end
175
- end
167
+ mac = client.hmac.digest([client.sequence_number, unencrypted_data].pack("NA*"))
176
168
 
177
- protected
169
+ encrypted_data = client.update_cipher(unencrypted_data) << client.final_cipher
178
170
 
179
- # Called when this module is used to extend an object. It initializes
180
- # the states and generally prepares the object for use as a packet stream.
181
- def initialize_ssh
182
- @hints = {}
183
- @server = State.new(self, :server)
184
- @client = State.new(self, :client)
185
- @packet = nil
186
- initialize_buffered_io
187
- end
171
+ message = encrypted_data + mac
172
+ end
173
+
174
+ debug { "queueing packet nr #{client.sequence_number} type #{payload.getbyte(0)} len #{packet_length}" }
175
+ enqueue(message)
188
176
 
189
- # Tries to read the next packet. If there is insufficient data to read
190
- # an entire packet, this returns immediately, otherwise the packet is
191
- # read, post-processed according to the cipher, hmac, and compression
192
- # algorithms specified in the server state object, and returned as a
193
- # new Packet object.
194
- def poll_next_packet
195
- if @packet.nil?
196
- minimum = server.block_size < 4 ? 4 : server.block_size
197
- return nil if available < minimum
198
- data = read_available(minimum)
199
-
200
- # decipher it
201
- @packet = Net::SSH::Buffer.new(server.update_cipher(data))
202
- @packet_length = @packet.read_long
177
+ client.increment(packet_length)
178
+
179
+ self
180
+ end
181
+
182
+ # Performs any pending cleanup necessary on the IO and its associated
183
+ # state objects. (See State#cleanup).
184
+ def cleanup
185
+ client.cleanup
186
+ server.cleanup
203
187
  end
204
188
 
205
- need = @packet_length + 4 - server.block_size
206
- raise Net::SSH::Exception, "padding error, need #{need} block #{server.block_size}" if need % server.block_size != 0
189
+ # If the IO object requires a rekey operation (as indicated by either its
190
+ # client or server state objects, see State#needs_rekey?), this will
191
+ # yield. Otherwise, this does nothing.
192
+ def if_needs_rekey?
193
+ if client.needs_rekey? || server.needs_rekey?
194
+ yield
195
+ client.reset! if client.needs_rekey?
196
+ server.reset! if server.needs_rekey?
197
+ end
198
+ end
207
199
 
208
- return nil if available < need + server.hmac.mac_length
200
+ protected
209
201
 
210
- if need > 0
211
- # read the remainder of the packet and decrypt it.
212
- data = read_available(need)
213
- @packet.append(server.update_cipher(data))
202
+ # Called when this module is used to extend an object. It initializes
203
+ # the states and generally prepares the object for use as a packet stream.
204
+ def initialize_ssh
205
+ @hints = {}
206
+ @server = State.new(self, :server)
207
+ @client = State.new(self, :client)
208
+ @packet = nil
209
+ initialize_buffered_io
214
210
  end
215
211
 
216
- # get the hmac from the tail of the packet (if one exists), and
217
- # then validate it.
218
- real_hmac = read_available(server.hmac.mac_length) || ""
212
+ # Tries to read the next packet. If there is insufficient data to read
213
+ # an entire packet, this returns immediately, otherwise the packet is
214
+ # read, post-processed according to the cipher, hmac, and compression
215
+ # algorithms specified in the server state object, and returned as a
216
+ # new Packet object.
217
+ # rubocop:disable Metrics/AbcSize
218
+ def poll_next_packet
219
+ aad_length = server.hmac.etm ? 4 : 0
220
+
221
+ if @packet.nil?
222
+ minimum = server.block_size < 4 ? 4 : server.block_size
223
+ return nil if available < minimum + aad_length
224
+
225
+ data = read_available(minimum + aad_length)
226
+
227
+ # decipher it
228
+ if server.hmac.etm
229
+ @packet_length = data.unpack("N").first
230
+ @mac_data = data
231
+ @packet = Net::SSH::Buffer.new(server.update_cipher(data[aad_length..-1]))
232
+ else
233
+ @packet = Net::SSH::Buffer.new(server.update_cipher(data))
234
+ @packet_length = @packet.read_long
235
+ end
236
+ end
237
+
238
+ need = @packet_length + 4 - aad_length - server.block_size
239
+ raise Net::SSH::Exception, "padding error, need #{need} block #{server.block_size}" if need % server.block_size != 0
240
+
241
+ return nil if available < need + server.hmac.mac_length
242
+
243
+ if need > 0
244
+ # read the remainder of the packet and decrypt it.
245
+ data = read_available(need)
246
+ @mac_data += data if server.hmac.etm
247
+ @packet.append(server.update_cipher(data))
248
+ end
249
+
250
+ # get the hmac from the tail of the packet (if one exists), and
251
+ # then validate it.
252
+ real_hmac = read_available(server.hmac.mac_length) || ""
219
253
 
220
- @packet.append(server.final_cipher)
221
- padding_length = @packet.read_byte
254
+ @packet.append(server.final_cipher)
255
+ padding_length = @packet.read_byte
222
256
 
223
- payload = @packet.read(@packet_length - padding_length - 1)
257
+ payload = @packet.read(@packet_length - padding_length - 1)
224
258
 
225
- my_computed_hmac = server.hmac.digest([server.sequence_number, @packet.content].pack("NA*"))
226
- raise Net::SSH::Exception, "corrupted mac detected" if real_hmac != my_computed_hmac
259
+ my_computed_hmac = if server.hmac.etm
260
+ server.hmac.digest([server.sequence_number, @mac_data].pack("NA*"))
261
+ else
262
+ server.hmac.digest([server.sequence_number, @packet.content].pack("NA*"))
263
+ end
264
+ raise Net::SSH::Exception, "corrupted hmac detected #{server.hmac.class}" if real_hmac != my_computed_hmac
227
265
 
228
- # try to decompress the payload, in case compression is active
229
- payload = server.decompress(payload)
266
+ # try to decompress the payload, in case compression is active
267
+ payload = server.decompress(payload)
230
268
 
231
- debug { "received packet nr #{server.sequence_number} type #{payload.getbyte(0)} len #{@packet_length}" }
269
+ debug { "received packet nr #{server.sequence_number} type #{payload.getbyte(0)} len #{@packet_length}" }
232
270
 
233
- server.increment(@packet_length)
234
- @packet = nil
271
+ server.increment(@packet_length)
272
+ @packet = nil
235
273
 
236
- return Packet.new(payload)
274
+ return Packet.new(payload)
275
+ end
237
276
  end
277
+ # rubocop:enable Metrics/AbcSize
278
+ end
238
279
  end
239
-
240
- end; end; end
280
+ end
@@ -2,77 +2,76 @@ require 'net/ssh/errors'
2
2
  require 'net/ssh/loggable'
3
3
  require 'net/ssh/version'
4
4
 
5
- module Net; module SSH; module Transport
5
+ module Net
6
+ module SSH
7
+ module Transport
8
+ # Negotiates the SSH protocol version and trades information about server
9
+ # and client. This is never used directly--it is always called by the
10
+ # transport layer as part of the initialization process of the transport
11
+ # layer.
12
+ #
13
+ # Note that this class also encapsulates the negotiated version, and acts as
14
+ # the authoritative reference for any queries regarding the version in effect.
15
+ class ServerVersion
16
+ include Loggable
6
17
 
7
- # Negotiates the SSH protocol version and trades information about server
8
- # and client. This is never used directly--it is always called by the
9
- # transport layer as part of the initialization process of the transport
10
- # layer.
11
- #
12
- # Note that this class also encapsulates the negotiated version, and acts as
13
- # the authoritative reference for any queries regarding the version in effect.
14
- class ServerVersion
15
- include Loggable
18
+ # The SSH version string as reported by Net::SSH
19
+ PROTO_VERSION = "SSH-2.0-Ruby/Net::SSH_#{Net::SSH::Version::CURRENT} #{RUBY_PLATFORM}"
16
20
 
17
- # The SSH version string as reported by Net::SSH
18
- PROTO_VERSION = "SSH-2.0-Ruby/Net::SSH_#{Net::SSH::Version::CURRENT} #{RUBY_PLATFORM}"
21
+ # Any header text sent by the server prior to sending the version.
22
+ attr_reader :header
19
23
 
20
- # Any header text sent by the server prior to sending the version.
21
- attr_reader :header
24
+ # The version string reported by the server.
25
+ attr_reader :version
22
26
 
23
- # The version string reported by the server.
24
- attr_reader :version
27
+ # Instantiates a new ServerVersion and immediately (and synchronously)
28
+ # negotiates the SSH protocol in effect, using the given socket.
29
+ def initialize(socket, logger, timeout = nil)
30
+ @header = String.new
31
+ @version = nil
32
+ @logger = logger
33
+ negotiate!(socket, timeout)
34
+ end
25
35
 
26
- # Instantiates a new ServerVersion and immediately (and synchronously)
27
- # negotiates the SSH protocol in effect, using the given socket.
28
- def initialize(socket, logger, timeout = nil)
29
- @header = ""
30
- @version = nil
31
- @logger = logger
32
- negotiate!(socket, timeout)
33
- end
36
+ private
34
37
 
35
- private
38
+ # Negotiates the SSH protocol to use, via the given socket. If the server
39
+ # reports an incompatible SSH version (e.g., SSH1), this will raise an
40
+ # exception.
41
+ def negotiate!(socket, timeout)
42
+ info { "negotiating protocol version" }
36
43
 
37
- # Negotiates the SSH protocol to use, via the given socket. If the server
38
- # reports an incompatible SSH version (e.g., SSH1), this will raise an
39
- # exception.
40
- def negotiate!(socket, timeout)
41
- info { "negotiating protocol version" }
44
+ debug { "local is `#{PROTO_VERSION}'" }
45
+ socket.write "#{PROTO_VERSION}\r\n"
46
+ socket.flush
42
47
 
43
- debug { "local is `#{PROTO_VERSION}'" }
44
- socket.write "#{PROTO_VERSION}\r\n"
45
- socket.flush
48
+ raise Net::SSH::ConnectionTimeout, "timeout during server version negotiating" if timeout && !IO.select([socket], nil, nil, timeout)
46
49
 
47
- if timeout && !IO.select([socket], nil, nil, timeout)
48
- raise Net::SSH::ConnectionTimeout, "timeout during server version negotiating"
49
- end
50
- loop do
51
- @version = ""
52
50
  loop do
53
- begin
54
- b = socket.readpartial(1)
55
- raise Net::SSH::Disconnect, "connection closed by remote host" if b.nil?
56
- rescue EOFError
57
- raise Net::SSH::Disconnect, "connection closed by remote host"
51
+ @version = String.new
52
+ loop do
53
+ begin
54
+ b = socket.readpartial(1)
55
+ raise Net::SSH::Disconnect, "connection closed by remote host" if b.nil?
56
+ rescue EOFError
57
+ raise Net::SSH::Disconnect, "connection closed by remote host"
58
+ end
59
+ @version << b
60
+ break if b == "\n"
58
61
  end
59
- @version << b
60
- break if b == "\n"
62
+ break if @version.match(/^SSH-/)
63
+
64
+ @header << @version
61
65
  end
62
- break if @version.match(/^SSH-/)
63
- @header << @version
64
- end
65
66
 
66
- @version.chomp!
67
- debug { "remote is `#{@version}'" }
67
+ @version.chomp!
68
+ debug { "remote is `#{@version}'" }
68
69
 
69
- unless @version.match(/^SSH-(1\.99|2\.0)-/)
70
- raise Net::SSH::Exception, "incompatible SSH version `#{@version}'"
71
- end
70
+ raise Net::SSH::Exception, "incompatible SSH version `#{@version}'" unless @version.match(/^SSH-(1\.99|2\.0)-/)
72
71
 
73
- if timeout && !IO.select(nil, [socket], nil, timeout)
74
- raise Net::SSH::ConnectionTimeout, "timeout during client version negotiating"
72
+ raise Net::SSH::ConnectionTimeout, "timeout during client version negotiating" if timeout && !IO.select(nil, [socket], nil, timeout)
75
73
  end
76
74
  end
75
+ end
77
76
  end
78
- end; end; end
77
+ end