net-ssh 5.0.2 → 7.0.1

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 (122) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  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 +13 -0
  8. data/.gitignore +3 -0
  9. data/.rubocop.yml +19 -2
  10. data/.rubocop_todo.yml +623 -511
  11. data/CHANGES.txt +76 -0
  12. data/Dockerfile +27 -0
  13. data/Dockerfile.openssl3 +17 -0
  14. data/Gemfile +2 -0
  15. data/Gemfile.noed25519 +2 -0
  16. data/Manifest +0 -1
  17. data/README.md +293 -0
  18. data/Rakefile +6 -2
  19. data/appveyor.yml +4 -2
  20. data/docker-compose.yml +23 -0
  21. data/lib/net/ssh/authentication/agent.rb +36 -14
  22. data/lib/net/ssh/authentication/certificate.rb +19 -7
  23. data/lib/net/ssh/authentication/constants.rb +0 -1
  24. data/lib/net/ssh/authentication/ed25519.rb +83 -50
  25. data/lib/net/ssh/authentication/ed25519_loader.rb +5 -8
  26. data/lib/net/ssh/authentication/key_manager.rb +74 -33
  27. data/lib/net/ssh/authentication/methods/abstract.rb +12 -3
  28. data/lib/net/ssh/authentication/methods/hostbased.rb +3 -5
  29. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +5 -3
  30. data/lib/net/ssh/authentication/methods/none.rb +6 -9
  31. data/lib/net/ssh/authentication/methods/password.rb +2 -3
  32. data/lib/net/ssh/authentication/methods/publickey.rb +58 -16
  33. data/lib/net/ssh/authentication/pageant.rb +97 -97
  34. data/lib/net/ssh/authentication/pub_key_fingerprint.rb +2 -3
  35. data/lib/net/ssh/authentication/session.rb +27 -23
  36. data/lib/net/ssh/buffer.rb +91 -40
  37. data/lib/net/ssh/buffered_io.rb +24 -26
  38. data/lib/net/ssh/config.rb +99 -53
  39. data/lib/net/ssh/connection/channel.rb +101 -87
  40. data/lib/net/ssh/connection/constants.rb +0 -4
  41. data/lib/net/ssh/connection/event_loop.rb +30 -25
  42. data/lib/net/ssh/connection/keepalive.rb +12 -12
  43. data/lib/net/ssh/connection/session.rb +115 -111
  44. data/lib/net/ssh/connection/term.rb +56 -58
  45. data/lib/net/ssh/errors.rb +12 -12
  46. data/lib/net/ssh/key_factory.rb +108 -22
  47. data/lib/net/ssh/known_hosts.rb +120 -36
  48. data/lib/net/ssh/loggable.rb +10 -11
  49. data/lib/net/ssh/packet.rb +1 -1
  50. data/lib/net/ssh/prompt.rb +9 -11
  51. data/lib/net/ssh/proxy/command.rb +1 -2
  52. data/lib/net/ssh/proxy/errors.rb +2 -4
  53. data/lib/net/ssh/proxy/http.rb +18 -20
  54. data/lib/net/ssh/proxy/https.rb +8 -10
  55. data/lib/net/ssh/proxy/jump.rb +8 -10
  56. data/lib/net/ssh/proxy/socks4.rb +2 -4
  57. data/lib/net/ssh/proxy/socks5.rb +3 -6
  58. data/lib/net/ssh/service/forward.rb +9 -8
  59. data/lib/net/ssh/test/channel.rb +24 -26
  60. data/lib/net/ssh/test/extensions.rb +37 -35
  61. data/lib/net/ssh/test/kex.rb +6 -8
  62. data/lib/net/ssh/test/local_packet.rb +0 -2
  63. data/lib/net/ssh/test/packet.rb +3 -3
  64. data/lib/net/ssh/test/remote_packet.rb +6 -8
  65. data/lib/net/ssh/test/script.rb +25 -27
  66. data/lib/net/ssh/test/socket.rb +12 -15
  67. data/lib/net/ssh/test.rb +12 -12
  68. data/lib/net/ssh/transport/algorithms.rb +177 -118
  69. data/lib/net/ssh/transport/cipher_factory.rb +34 -50
  70. data/lib/net/ssh/transport/constants.rb +13 -9
  71. data/lib/net/ssh/transport/ctr.rb +8 -14
  72. data/lib/net/ssh/transport/hmac/abstract.rb +20 -5
  73. data/lib/net/ssh/transport/hmac/md5.rb +0 -2
  74. data/lib/net/ssh/transport/hmac/md5_96.rb +0 -2
  75. data/lib/net/ssh/transport/hmac/none.rb +0 -2
  76. data/lib/net/ssh/transport/hmac/ripemd160.rb +0 -2
  77. data/lib/net/ssh/transport/hmac/sha1.rb +0 -2
  78. data/lib/net/ssh/transport/hmac/sha1_96.rb +0 -2
  79. data/lib/net/ssh/transport/hmac/sha2_256.rb +7 -11
  80. data/lib/net/ssh/transport/hmac/sha2_256_96.rb +4 -8
  81. data/lib/net/ssh/transport/hmac/sha2_256_etm.rb +12 -0
  82. data/lib/net/ssh/transport/hmac/sha2_512.rb +6 -9
  83. data/lib/net/ssh/transport/hmac/sha2_512_96.rb +4 -8
  84. data/lib/net/ssh/transport/hmac/sha2_512_etm.rb +12 -0
  85. data/lib/net/ssh/transport/hmac.rb +13 -11
  86. data/lib/net/ssh/transport/identity_cipher.rb +11 -13
  87. data/lib/net/ssh/transport/kex/abstract.rb +130 -0
  88. data/lib/net/ssh/transport/kex/abstract5656.rb +72 -0
  89. data/lib/net/ssh/transport/kex/curve25519_sha256.rb +39 -0
  90. data/lib/net/ssh/transport/kex/curve25519_sha256_loader.rb +30 -0
  91. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +5 -19
  92. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha256.rb +11 -0
  93. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +30 -139
  94. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +1 -8
  95. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha256.rb +5 -9
  96. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +20 -81
  97. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +5 -4
  98. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +5 -4
  99. data/lib/net/ssh/transport/kex.rb +15 -10
  100. data/lib/net/ssh/transport/key_expander.rb +7 -8
  101. data/lib/net/ssh/transport/openssl.rb +149 -111
  102. data/lib/net/ssh/transport/packet_stream.rb +53 -22
  103. data/lib/net/ssh/transport/server_version.rb +17 -16
  104. data/lib/net/ssh/transport/session.rb +35 -11
  105. data/lib/net/ssh/transport/state.rb +44 -44
  106. data/lib/net/ssh/verifiers/accept_new.rb +7 -2
  107. data/lib/net/ssh/verifiers/accept_new_or_local_tunnel.rb +1 -2
  108. data/lib/net/ssh/verifiers/always.rb +10 -4
  109. data/lib/net/ssh/verifiers/never.rb +4 -2
  110. data/lib/net/ssh/version.rb +2 -2
  111. data/lib/net/ssh.rb +17 -9
  112. data/net-ssh-public_cert.pem +18 -19
  113. data/net-ssh.gemspec +9 -7
  114. data/support/ssh_tunnel_bug.rb +3 -3
  115. data.tar.gz.sig +0 -0
  116. metadata +65 -41
  117. metadata.gz.sig +0 -0
  118. data/.travis.yml +0 -52
  119. data/Gemfile.noed25519.lock +0 -41
  120. data/README.rdoc +0 -169
  121. data/lib/net/ssh/ruby_compat.rb +0 -13
  122. data/support/arcfour_check.rb +0 -20
@@ -1,6 +1,5 @@
1
1
  module Net
2
2
  module SSH
3
-
4
3
  # The Net::SSH::Config class is used to parse OpenSSH configuration files,
5
4
  # and translates that syntax into the configuration syntax that Net::SSH
6
5
  # understands. This lets Net::SSH scripts read their configuration (to
@@ -11,6 +10,7 @@ module Net
11
10
  #
12
11
  # * ChallengeResponseAuthentication => maps to the :auth_methods option challenge-response (then coleasced into keyboard-interactive)
13
12
  # * KbdInteractiveAuthentication => maps to the :auth_methods keyboard-interactive
13
+ # * CertificateFile => maps to the :keycerts option
14
14
  # * Ciphers => maps to the :encryption option
15
15
  # * Compression => :compression
16
16
  # * CompressionLevel => :compression_level
@@ -22,7 +22,9 @@ module Net
22
22
  # * HostKeyAlias => :host_key_alias
23
23
  # * HostName => :host_name
24
24
  # * IdentityFile => maps to the :keys option
25
+ # * IdentityAgent => :identity_agent
25
26
  # * IdentitiesOnly => :keys_only
27
+ # * CheckHostIP => :check_host_ip
26
28
  # * Macs => maps to the :hmac option
27
29
  # * PasswordAuthentication => maps to the :auth_methods option password
28
30
  # * Port => :port
@@ -31,6 +33,7 @@ module Net
31
33
  # * ProxyJump => maps to the :proxy option
32
34
  # * PubKeyAuthentication => maps to the :auth_methods option
33
35
  # * RekeyLimit => :rekey_limit
36
+ # * StrictHostKeyChecking => :verify_host_key
34
37
  # * User => :user
35
38
  # * UserKnownHostsFile => :user_known_hosts_file
36
39
  # * NumberOfPasswordPrompts => :number_of_password_prompts
@@ -62,7 +65,7 @@ module Net
62
65
  # given +files+ (defaulting to the list of files returned by
63
66
  # #default_files), translates the resulting hash into the options
64
67
  # recognized by Net::SSH, and returns them.
65
- def for(host, files=expandable_default_files)
68
+ def for(host, files = expandable_default_files)
66
69
  translate(files.inject({}) { |settings, file|
67
70
  load(file, host, settings)
68
71
  })
@@ -74,7 +77,7 @@ module Net
74
77
  # ones. Returns a hash containing the OpenSSH options. (See
75
78
  # #translate for how to convert the OpenSSH options into Net::SSH
76
79
  # options.)
77
- def load(path, host, settings={}, base_dir = nil)
80
+ def load(path, host, settings = {}, base_dir = nil)
78
81
  file = File.expand_path(path)
79
82
  base_dir ||= File.dirname(file)
80
83
  return settings unless File.readable?(file)
@@ -95,7 +98,7 @@ module Net
95
98
  next if value.nil?
96
99
 
97
100
  key.downcase!
98
- value = $1 if value =~ /^"(.*)"$/
101
+ value = unquote(value)
99
102
 
100
103
  value = case value.strip
101
104
  when /^\d+$/ then value.to_i
@@ -126,7 +129,7 @@ module Net
126
129
  block_seen = true
127
130
  elsif !block_seen
128
131
  case key
129
- when 'identityfile'
132
+ when 'identityfile', 'certificatefile'
130
133
  (globals[key] ||= []) << value
131
134
  when 'include'
132
135
  included_file_paths(base_dir, value).each do |file_path|
@@ -137,7 +140,7 @@ module Net
137
140
  end
138
141
  elsif block_matched
139
142
  case key
140
- when 'identityfile'
143
+ when 'identityfile', 'certificatefile'
141
144
  (settings[key] ||= []) << value
142
145
  when 'include'
143
146
  included_file_paths(base_dir, value).each do |file_path|
@@ -147,11 +150,18 @@ module Net
147
150
  settings[key] = value unless settings.key?(key)
148
151
  end
149
152
  end
153
+
154
+ # ProxyCommand and ProxyJump override each other so they need to be tracked togeather
155
+ %w[proxyjump proxycommand].each do |proxy_key|
156
+ if (proxy_value = settings.delete(proxy_key))
157
+ settings['proxy'] ||= [proxy_key, proxy_value]
158
+ end
159
+ end
150
160
  end
151
161
 
152
162
  globals.merge(settings) do |key, oldval, newval|
153
163
  case key
154
- when 'identityfile'
164
+ when 'identityfile', 'certificatefile'
155
165
  oldval + newval
156
166
  else
157
167
  newval
@@ -175,34 +185,57 @@ module Net
175
185
  # Filters default_files down to the files that are expandable.
176
186
  def expandable_default_files
177
187
  default_files.keep_if do |path|
178
- begin
179
- File.expand_path(path)
180
- true
181
- rescue ArgumentError
182
- false
183
- end
188
+ File.expand_path(path)
189
+ true
190
+ rescue ArgumentError
191
+ false
184
192
  end
185
193
  end
186
194
 
187
195
  private
188
196
 
197
+ def translate_verify_host_key(value)
198
+ case value
199
+ when false
200
+ :never
201
+ when true
202
+ :always
203
+ when 'accept-new'
204
+ :accept_new
205
+ end
206
+ end
207
+
208
+ def translate_keepalive(hash, value)
209
+ if value && value.to_i > 0
210
+ hash[:keepalive] = true
211
+ hash[:keepalive_interval] = value.to_i
212
+ else
213
+ hash[:keepalive] = false
214
+ end
215
+ end
216
+
217
+ TRANSLATE_CONFIG_KEY_RENAME_MAP = {
218
+ bindaddress: :bind_address,
219
+ compression: :compression,
220
+ compressionlevel: :compression_level,
221
+ certificatefile: :keycerts,
222
+ connecttimeout: :timeout,
223
+ forwardagent: :forward_agent,
224
+ identitiesonly: :keys_only,
225
+ identityagent: :identity_agent,
226
+ globalknownhostsfile: :global_known_hosts_file,
227
+ hostkeyalias: :host_key_alias,
228
+ identityfile: :keys,
229
+ fingerprinthash: :fingerprint_hash,
230
+ port: :port,
231
+ user: :user,
232
+ userknownhostsfile: :user_known_hosts_file,
233
+ checkhostip: :check_host_ip
234
+ }.freeze
189
235
  def translate_config_key(hash, key, value, settings)
190
- rename = {
191
- bindaddress: :bind_address,
192
- compression: :compression,
193
- compressionlevel: :compression_level,
194
- connecttimeout: :timeout,
195
- forwardagent: :forward_agent,
196
- identitiesonly: :keys_only,
197
- globalknownhostsfile: :global_known_hosts_file,
198
- hostkeyalias: :host_key_alias,
199
- identityfile: :keys,
200
- fingerprinthash: :fingerprint_hash,
201
- port: :port,
202
- user: :user,
203
- userknownhostsfile: :user_known_hosts_file
204
- }
205
236
  case key
237
+ when :stricthostkeychecking
238
+ hash[:verify_host_key] = translate_verify_host_key(value)
206
239
  when :ciphers
207
240
  hash[:encryption] = value.split(/,/)
208
241
  when :hostbasedauthentication
@@ -220,12 +253,7 @@ module Net
220
253
  when :serveralivecountmax
221
254
  hash[:keepalive_maxcount] = value.to_i if value
222
255
  when :serveraliveinterval
223
- if value && value.to_i > 0
224
- hash[:keepalive] = true
225
- hash[:keepalive_interval] = value.to_i
226
- else
227
- hash[:keepalive] = false
228
- end
256
+ translate_keepalive(hash, value)
229
257
  when :passwordauthentication
230
258
  if value
231
259
  (hash[:auth_methods] << 'password').uniq!
@@ -246,15 +274,9 @@ module Net
246
274
  end
247
275
  when :preferredauthentications
248
276
  hash[:auth_methods] = value.split(/,/) # TODO we should place to preferred_auth_methods rather than auth_methods
249
- when :proxycommand
250
- if value and value !~ /^none$/
251
- require 'net/ssh/proxy/command'
252
- hash[:proxy] = Net::SSH::Proxy::Command.new(value)
253
- end
254
- when :proxyjump
255
- if value
256
- require 'net/ssh/proxy/jump'
257
- hash[:proxy] = Net::SSH::Proxy::Jump.new(value)
277
+ when :proxy
278
+ if (proxy = setup_proxy(*value))
279
+ hash[:proxy] = proxy
258
280
  end
259
281
  when :pubkeyauthentication
260
282
  if value
@@ -267,10 +289,25 @@ module Net
267
289
  when :sendenv
268
290
  multi_send_env = value.to_s.split(/\s+/)
269
291
  hash[:send_env] = multi_send_env.map { |e| Regexp.new pattern2regex(e).source, false }
292
+ when :setenv
293
+ hash[:set_env] = Shellwords.split(value.to_s).map { |e| e.split '=', 2 }.to_h
270
294
  when :numberofpasswordprompts
271
295
  hash[:number_of_password_prompts] = value.to_i
272
- when *rename.keys
273
- hash[rename[key]] = value
296
+ when *TRANSLATE_CONFIG_KEY_RENAME_MAP.keys
297
+ hash[TRANSLATE_CONFIG_KEY_RENAME_MAP[key]] = value
298
+ end
299
+ end
300
+
301
+ def setup_proxy(type, value)
302
+ case type
303
+ when 'proxycommand'
304
+ if value !~ /^none$/
305
+ require 'net/ssh/proxy/command'
306
+ Net::SSH::Proxy::Command.new(value)
307
+ end
308
+ when 'proxyjump'
309
+ require 'net/ssh/proxy/jump'
310
+ Net::SSH::Proxy::Jump.new(value)
274
311
  end
275
312
  end
276
313
 
@@ -278,9 +315,9 @@ module Net
278
315
  # host names.
279
316
  def pattern2regex(pattern)
280
317
  tail = pattern
281
- prefix = ""
318
+ prefix = String.new
282
319
  while !tail.empty? do
283
- head,sep,tail = tail.partition(/[\*\?]/)
320
+ head, sep, tail = tail.partition(/[\*\?]/)
284
321
  prefix = prefix + Regexp.quote(head)
285
322
  case sep
286
323
  when '*'
@@ -326,12 +363,17 @@ module Net
326
363
  end
327
364
 
328
365
  def eval_match_conditions(condition, host, settings)
329
- conditions = condition.split(/\s+/)
366
+ # Not using `\s` for whitespace matching as canonical
367
+ # ssh_config parser implementation (OpenSSH) has specific character set.
368
+ # Ref: https://github.com/openssh/openssh-portable/blob/2581333d564d8697837729b3d07d45738eaf5a54/misc.c#L237-L239
369
+ conditions = condition.split(/[ \t\r\n]+|(?<!=)=(?!=)/).reject(&:empty?)
330
370
  return true if conditions == ["all"]
331
371
 
332
372
  conditions = conditions.each_slice(2)
333
- matching = true
334
- conditions.each do |(kind,exprs)|
373
+ condition_matches = []
374
+ conditions.each do |(kind, exprs)|
375
+ exprs = unquote(exprs)
376
+
335
377
  case kind.downcase
336
378
  when "all"
337
379
  raise "all cannot be mixed with other conditions"
@@ -346,15 +388,19 @@ module Net
346
388
  exprs.split(",").each do |expr|
347
389
  condition_met = condition_met || host =~ pattern2regex(expr)
348
390
  end
349
- matching = matching && negated ^ condition_met
391
+ condition_matches << (true && negated ^ condition_met)
350
392
  # else
351
393
  # warn "net-ssh: Unsupported expr in Match block: #{kind}"
352
394
  end
353
395
  end
354
- matching
396
+
397
+ !condition_matches.empty? && condition_matches.all?
398
+ end
399
+
400
+ def unquote(string)
401
+ string =~ /^"(.*)"$/ ? Regexp.last_match(1) : string
355
402
  end
356
403
  end
357
404
  end
358
-
359
405
  end
360
406
  end