net-ssh 6.1.0 → 7.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +2 -1
  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 +2 -0
  9. data/.rubocop.yml +12 -1
  10. data/.rubocop_todo.yml +474 -375
  11. data/CHANGES.txt +40 -3
  12. data/Dockerfile +27 -0
  13. data/Dockerfile.openssl3 +17 -0
  14. data/Gemfile +2 -0
  15. data/Gemfile.noed25519 +2 -0
  16. data/README.md +14 -4
  17. data/Rakefile +59 -0
  18. data/SECURITY.md +4 -0
  19. data/docker-compose.yml +23 -0
  20. data/lib/net/ssh/authentication/agent.rb +29 -13
  21. data/lib/net/ssh/authentication/certificate.rb +14 -11
  22. data/lib/net/ssh/authentication/constants.rb +0 -1
  23. data/lib/net/ssh/authentication/ed25519.rb +12 -7
  24. data/lib/net/ssh/authentication/ed25519_loader.rb +4 -7
  25. data/lib/net/ssh/authentication/key_manager.rb +46 -34
  26. data/lib/net/ssh/authentication/methods/abstract.rb +12 -3
  27. data/lib/net/ssh/authentication/methods/hostbased.rb +3 -5
  28. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +2 -2
  29. data/lib/net/ssh/authentication/methods/none.rb +6 -9
  30. data/lib/net/ssh/authentication/methods/password.rb +2 -3
  31. data/lib/net/ssh/authentication/methods/publickey.rb +56 -16
  32. data/lib/net/ssh/authentication/pageant.rb +97 -97
  33. data/lib/net/ssh/authentication/pub_key_fingerprint.rb +2 -2
  34. data/lib/net/ssh/authentication/session.rb +18 -17
  35. data/lib/net/ssh/buffer.rb +71 -51
  36. data/lib/net/ssh/buffered_io.rb +24 -25
  37. data/lib/net/ssh/config.rb +33 -20
  38. data/lib/net/ssh/connection/channel.rb +84 -82
  39. data/lib/net/ssh/connection/constants.rb +0 -4
  40. data/lib/net/ssh/connection/event_loop.rb +30 -24
  41. data/lib/net/ssh/connection/keepalive.rb +12 -12
  42. data/lib/net/ssh/connection/session.rb +109 -108
  43. data/lib/net/ssh/connection/term.rb +56 -58
  44. data/lib/net/ssh/errors.rb +12 -12
  45. data/lib/net/ssh/key_factory.rb +7 -8
  46. data/lib/net/ssh/known_hosts.rb +84 -15
  47. data/lib/net/ssh/loggable.rb +8 -9
  48. data/lib/net/ssh/packet.rb +1 -1
  49. data/lib/net/ssh/prompt.rb +9 -11
  50. data/lib/net/ssh/proxy/command.rb +1 -1
  51. data/lib/net/ssh/proxy/errors.rb +2 -4
  52. data/lib/net/ssh/proxy/http.rb +18 -20
  53. data/lib/net/ssh/proxy/https.rb +8 -10
  54. data/lib/net/ssh/proxy/jump.rb +8 -10
  55. data/lib/net/ssh/proxy/socks4.rb +2 -4
  56. data/lib/net/ssh/proxy/socks5.rb +3 -5
  57. data/lib/net/ssh/service/forward.rb +7 -7
  58. data/lib/net/ssh/test/channel.rb +24 -26
  59. data/lib/net/ssh/test/extensions.rb +35 -35
  60. data/lib/net/ssh/test/kex.rb +6 -8
  61. data/lib/net/ssh/test/local_packet.rb +0 -2
  62. data/lib/net/ssh/test/packet.rb +3 -3
  63. data/lib/net/ssh/test/remote_packet.rb +6 -8
  64. data/lib/net/ssh/test/script.rb +25 -27
  65. data/lib/net/ssh/test/socket.rb +12 -15
  66. data/lib/net/ssh/test.rb +4 -5
  67. data/lib/net/ssh/transport/algorithms.rb +17 -14
  68. data/lib/net/ssh/transport/cipher_factory.rb +28 -28
  69. data/lib/net/ssh/transport/constants.rb +3 -3
  70. data/lib/net/ssh/transport/ctr.rb +7 -7
  71. data/lib/net/ssh/transport/hmac/abstract.rb +4 -5
  72. data/lib/net/ssh/transport/hmac/md5.rb +0 -2
  73. data/lib/net/ssh/transport/hmac/md5_96.rb +0 -2
  74. data/lib/net/ssh/transport/hmac/none.rb +0 -2
  75. data/lib/net/ssh/transport/hmac/ripemd160.rb +0 -2
  76. data/lib/net/ssh/transport/hmac/sha1.rb +0 -2
  77. data/lib/net/ssh/transport/hmac/sha1_96.rb +0 -2
  78. data/lib/net/ssh/transport/hmac.rb +12 -12
  79. data/lib/net/ssh/transport/identity_cipher.rb +11 -13
  80. data/lib/net/ssh/transport/kex/abstract.rb +12 -5
  81. data/lib/net/ssh/transport/kex/abstract5656.rb +1 -1
  82. data/lib/net/ssh/transport/kex/curve25519_sha256.rb +2 -1
  83. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +4 -4
  84. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha256.rb +11 -0
  85. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +21 -21
  86. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +1 -2
  87. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +2 -2
  88. data/lib/net/ssh/transport/kex.rb +8 -6
  89. data/lib/net/ssh/transport/key_expander.rb +7 -8
  90. data/lib/net/ssh/transport/openssl.rb +51 -26
  91. data/lib/net/ssh/transport/packet_stream.rb +2 -3
  92. data/lib/net/ssh/transport/server_version.rb +17 -16
  93. data/lib/net/ssh/transport/session.rb +9 -7
  94. data/lib/net/ssh/transport/state.rb +43 -43
  95. data/lib/net/ssh/verifiers/accept_new.rb +0 -2
  96. data/lib/net/ssh/verifiers/accept_new_or_local_tunnel.rb +1 -2
  97. data/lib/net/ssh/verifiers/always.rb +6 -4
  98. data/lib/net/ssh/verifiers/never.rb +0 -2
  99. data/lib/net/ssh/version.rb +1 -1
  100. data/lib/net/ssh.rb +10 -6
  101. data/net-ssh-public_cert.pem +8 -8
  102. data/net-ssh.gemspec +2 -2
  103. data/support/ssh_tunnel_bug.rb +3 -3
  104. data.tar.gz.sig +0 -0
  105. metadata +24 -15
  106. metadata.gz.sig +0 -0
  107. data/.travis.yml +0 -52
@@ -5,7 +5,6 @@ require 'net/ssh/authentication/ed25519_loader'
5
5
 
6
6
  module Net
7
7
  module SSH
8
-
9
8
  # Net::SSH::Buffer is a flexible class for building and parsing binary
10
9
  # data packets. It provides a stream-like interface for sequentially
11
10
  # reading data items from the buffer, as well as a useful helper method
@@ -71,7 +70,7 @@ module Net
71
70
 
72
71
  # Creates a new buffer, initialized to the given content. The position
73
72
  # is initialized to the beginning of the buffer.
74
- def initialize(content="")
73
+ def initialize(content = String.new)
75
74
  @content = content.to_s
76
75
  @position = 0
77
76
  end
@@ -118,7 +117,7 @@ module Net
118
117
  # Resets the buffer, making it empty. Also, resets the read position to
119
118
  # 0.
120
119
  def clear!
121
- @content = ""
120
+ @content = String.new
122
121
  @position = 0
123
122
  end
124
123
 
@@ -129,12 +128,12 @@ module Net
129
128
  # would otherwise tend to grow without bound.
130
129
  #
131
130
  # Returns the buffer object itself.
132
- def consume!(n=position)
131
+ def consume!(n = position)
133
132
  if n >= length
134
133
  # optimize for a fairly common case
135
134
  clear!
136
135
  elsif n > 0
137
- @content = @content[n..-1] || ""
136
+ @content = @content[n..-1] || String.new
138
137
  @position -= n
139
138
  @position = 0 if @position < 0
140
139
  end
@@ -172,7 +171,7 @@ module Net
172
171
  # Reads and returns the next +count+ bytes from the buffer, starting from
173
172
  # the read position. If +count+ is +nil+, this will return all remaining
174
173
  # text in the buffer. This method will increment the pointer.
175
- def read(count=nil)
174
+ def read(count = nil)
176
175
  count ||= length
177
176
  count = length - @position if @position + count > length
178
177
  @position += count
@@ -181,7 +180,7 @@ module Net
181
180
 
182
181
  # Reads (as #read) and returns the given number of bytes from the buffer,
183
182
  # and then consumes (as #consume!) all data up to the new read position.
184
- def read!(count=nil)
183
+ def read!(count = nil)
185
184
  data = read(count)
186
185
  consume!
187
186
  data
@@ -237,6 +236,7 @@ module Net
237
236
  def read_bignum
238
237
  data = read_string
239
238
  return unless data
239
+
240
240
  OpenSSL::BN.new(data, 2)
241
241
  end
242
242
 
@@ -251,7 +251,6 @@ module Net
251
251
  def read_private_keyblob(type)
252
252
  case type
253
253
  when /^ssh-rsa$/
254
- key = OpenSSL::PKey::RSA.new
255
254
  n = read_bignum
256
255
  e = read_bignum
257
256
  d = read_bignum
@@ -262,27 +261,30 @@ module Net
262
261
  _unkown2 = read_bignum
263
262
  dmp1 = d % (p - 1)
264
263
  dmq1 = d % (q - 1)
265
- if key.respond_to?(:set_key)
266
- key.set_key(n, e, d)
267
- else
268
- key.e = e
269
- key.n = n
270
- key.d = d
271
- end
272
- if key.respond_to?(:set_factors)
273
- key.set_factors(p, q)
274
- else
275
- key.p = p
276
- key.q = q
277
- end
278
- if key.respond_to?(:set_crt_params)
279
- key.set_crt_params(dmp1, dmq1, iqmp)
280
- else
281
- key.dmp1 = dmp1
282
- key.dmq1 = dmq1
283
- key.iqmp = iqmp
264
+ # Public key
265
+ data_sequence = OpenSSL::ASN1::Sequence([
266
+ OpenSSL::ASN1::Integer(n),
267
+ OpenSSL::ASN1::Integer(e)
268
+ ])
269
+
270
+ if d && p && q && dmp1 && dmq1 && iqmp
271
+ data_sequence = OpenSSL::ASN1::Sequence([
272
+ OpenSSL::ASN1::Integer(0),
273
+ OpenSSL::ASN1::Integer(n),
274
+ OpenSSL::ASN1::Integer(e),
275
+ OpenSSL::ASN1::Integer(d),
276
+ OpenSSL::ASN1::Integer(p),
277
+ OpenSSL::ASN1::Integer(q),
278
+ OpenSSL::ASN1::Integer(dmp1),
279
+ OpenSSL::ASN1::Integer(dmq1),
280
+ OpenSSL::ASN1::Integer(iqmp)
281
+ ])
284
282
  end
285
- key
283
+
284
+ asn1 = OpenSSL::ASN1::Sequence(data_sequence)
285
+ OpenSSL::PKey::RSA.new(asn1.to_der)
286
+ when /^ecdsa\-sha2\-(\w*)$/
287
+ OpenSSL::PKey::EC.read_keyblob($1, self)
286
288
  else
287
289
  raise Exception, "Cannot decode private key of type #{type}"
288
290
  end
@@ -295,29 +297,42 @@ module Net
295
297
  when /^(.*)-cert-v01@openssh\.com$/
296
298
  key = Net::SSH::Authentication::Certificate.read_certblob(self, $1)
297
299
  when /^ssh-dss$/
298
- key = OpenSSL::PKey::DSA.new
299
- if key.respond_to?(:set_pqg)
300
- key.set_pqg(read_bignum, read_bignum, read_bignum)
301
- else
302
- key.p = read_bignum
303
- key.q = read_bignum
304
- key.g = read_bignum
305
- end
306
- if key.respond_to?(:set_key)
307
- key.set_key(read_bignum, nil)
308
- else
309
- key.pub_key = read_bignum
310
- end
300
+ p = read_bignum
301
+ q = read_bignum
302
+ g = read_bignum
303
+ pub_key = read_bignum
304
+
305
+ asn1 = OpenSSL::ASN1::Sequence.new(
306
+ [
307
+ OpenSSL::ASN1::Sequence.new(
308
+ [
309
+ OpenSSL::ASN1::ObjectId.new('DSA'),
310
+ OpenSSL::ASN1::Sequence.new(
311
+ [
312
+ OpenSSL::ASN1::Integer.new(p),
313
+ OpenSSL::ASN1::Integer.new(q),
314
+ OpenSSL::ASN1::Integer.new(g)
315
+ ]
316
+ )
317
+ ]
318
+ ),
319
+ OpenSSL::ASN1::BitString.new(OpenSSL::ASN1::Integer.new(pub_key).to_der)
320
+ ]
321
+ )
322
+
323
+ key = OpenSSL::PKey::DSA.new(asn1.to_der)
311
324
  when /^ssh-rsa$/
312
- key = OpenSSL::PKey::RSA.new
313
- if key.respond_to?(:set_key)
314
- e = read_bignum
315
- n = read_bignum
316
- key.set_key(n, e, nil)
317
- else
318
- key.e = read_bignum
319
- key.n = read_bignum
320
- end
325
+ e = read_bignum
326
+ n = read_bignum
327
+
328
+ asn1 = OpenSSL::ASN1::Sequence(
329
+ [
330
+ OpenSSL::ASN1::Integer(n),
331
+ OpenSSL::ASN1::Integer(e)
332
+ ]
333
+ )
334
+
335
+ key = OpenSSL::PKey::RSA.new(asn1.to_der)
321
336
  when /^ssh-ed25519$/
322
337
  Net::SSH::Authentication::ED25519Loader.raiseUnlessLoaded("unsupported key type `#{type}'")
323
338
  key = Net::SSH::Authentication::ED25519::PubKey.read_keyblob(self)
@@ -346,7 +361,12 @@ module Net
346
361
  # Optimized version of write where the caller gives up ownership of string
347
362
  # to the method. This way we can mutate the string.
348
363
  def write_moved(string)
349
- @content << string.force_encoding('BINARY')
364
+ @content <<
365
+ if string.frozen?
366
+ string.dup.force_encoding('BINARY')
367
+ else
368
+ string.force_encoding('BINARY')
369
+ end
350
370
  self
351
371
  end
352
372
 
@@ -1,9 +1,8 @@
1
1
  require 'net/ssh/buffer'
2
2
  require 'net/ssh/loggable'
3
3
 
4
- module Net
4
+ module Net
5
5
  module SSH
6
-
7
6
  # This module is used to extend sockets and other IO objects, to allow
8
7
  # them to be buffered for both read and write. This abstraction makes it
9
8
  # quite easy to write a select-based event loop
@@ -48,19 +47,19 @@ module Net
48
47
  # end
49
48
  module BufferedIo
50
49
  include Loggable
51
-
50
+
52
51
  # Called when the #extend is called on an object, with this module as the
53
52
  # argument. It ensures that the modules instance variables are all properly
54
53
  # initialized.
55
- def self.extended(object) #:nodoc:
54
+ def self.extended(object) # :nodoc:
56
55
  # need to use __send__ because #send is overridden in Socket
57
56
  object.__send__(:initialize_buffered_io)
58
57
  end
59
-
58
+
60
59
  # Tries to read up to +n+ bytes of data from the remote end, and appends
61
60
  # the data to the input buffer. It returns the number of bytes read, or 0
62
61
  # if no data was available to be read.
63
- def fill(n=8192)
62
+ def fill(n = 8192)
64
63
  input.consume!
65
64
  data = recv(n)
66
65
  debug { "read #{data.length} bytes" }
@@ -70,31 +69,31 @@ module Net
70
69
  @input_errors << e
71
70
  return 0
72
71
  end
73
-
72
+
74
73
  # Read up to +length+ bytes from the input buffer. If +length+ is nil,
75
74
  # all available data is read from the buffer. (See #available.)
76
- def read_available(length=nil)
75
+ def read_available(length = nil)
77
76
  input.read(length || available)
78
77
  end
79
-
78
+
80
79
  # Returns the number of bytes available to be read from the input buffer.
81
80
  # (See #read_available.)
82
81
  def available
83
82
  input.available
84
83
  end
85
-
84
+
86
85
  # Enqueues data in the output buffer, to be written when #send_pending
87
86
  # is called. Note that the data is _not_ sent immediately by this method!
88
87
  def enqueue(data)
89
88
  output.append(data)
90
89
  end
91
-
90
+
92
91
  # Returns +true+ if there is data waiting in the output buffer, and
93
92
  # +false+ otherwise.
94
93
  def pending_write?
95
94
  output.length > 0
96
95
  end
97
-
96
+
98
97
  # Sends as much of the pending output as possible. Returns +true+ if any
99
98
  # data was sent, and +false+ otherwise.
100
99
  def send_pending
@@ -107,7 +106,7 @@ module Net
107
106
  return false
108
107
  end
109
108
  end
110
-
109
+
111
110
  # Calls #send_pending repeatedly, if necessary, blocking until the output
112
111
  # buffer is empty.
113
112
  def wait_for_pending_sends
@@ -115,31 +114,32 @@ module Net
115
114
  while output.length > 0
116
115
  result = IO.select(nil, [self]) or next
117
116
  next unless result[1].any?
117
+
118
118
  send_pending
119
119
  end
120
120
  end
121
-
121
+
122
122
  public # these methods are primarily for use in tests
123
-
124
- def write_buffer #:nodoc:
123
+
124
+ def write_buffer # :nodoc:
125
125
  output.to_s
126
126
  end
127
-
128
- def read_buffer #:nodoc:
127
+
128
+ def read_buffer # :nodoc:
129
129
  input.to_s
130
130
  end
131
-
131
+
132
132
  private
133
-
133
+
134
134
  #--
135
135
  # Can't use attr_reader here (after +private+) without incurring the
136
136
  # wrath of "ruby -w". We hates it.
137
137
  #++
138
-
138
+
139
139
  def input; @input; end
140
140
 
141
141
  def output; @output; end
142
-
142
+
143
143
  # Initializes the intput and output buffers for this object. This method
144
144
  # is called automatically when the module is mixed into an object via
145
145
  # Object#extend (see Net::SSH::BufferedIo.extended), but must be called
@@ -166,7 +166,7 @@ module Net
166
166
  # http://github.com/net-ssh/net-ssh/tree/portfwfix
167
167
  #
168
168
  module ForwardedBufferedIo
169
- def fill(n=8192)
169
+ def fill(n = 8192)
170
170
  begin
171
171
  super(n)
172
172
  rescue Errno::ECONNRESET => e
@@ -181,7 +181,7 @@ module Net
181
181
  end
182
182
  end
183
183
  end
184
-
184
+
185
185
  def send_pending
186
186
  begin
187
187
  super
@@ -198,6 +198,5 @@ module Net
198
198
  end
199
199
  end
200
200
  end
201
-
202
201
  end
203
202
  end
@@ -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
@@ -34,7 +33,7 @@ module Net
34
33
  # * ProxyJump => maps to the :proxy option
35
34
  # * PubKeyAuthentication => maps to the :auth_methods option
36
35
  # * RekeyLimit => :rekey_limit
37
- # * StrictHostKeyChecking => :strict_host_key_checking
36
+ # * StrictHostKeyChecking => :verify_host_key
38
37
  # * User => :user
39
38
  # * UserKnownHostsFile => :user_known_hosts_file
40
39
  # * NumberOfPasswordPrompts => :number_of_password_prompts
@@ -66,7 +65,7 @@ module Net
66
65
  # given +files+ (defaulting to the list of files returned by
67
66
  # #default_files), translates the resulting hash into the options
68
67
  # recognized by Net::SSH, and returns them.
69
- def for(host, files=expandable_default_files)
68
+ def for(host, files = expandable_default_files)
70
69
  translate(files.inject({}) { |settings, file|
71
70
  load(file, host, settings)
72
71
  })
@@ -78,7 +77,7 @@ module Net
78
77
  # ones. Returns a hash containing the OpenSSH options. (See
79
78
  # #translate for how to convert the OpenSSH options into Net::SSH
80
79
  # options.)
81
- def load(path, host, settings={}, base_dir = nil)
80
+ def load(path, host, settings = {}, base_dir = nil)
82
81
  file = File.expand_path(path)
83
82
  base_dir ||= File.dirname(file)
84
83
  return settings unless File.readable?(file)
@@ -186,17 +185,35 @@ module Net
186
185
  # Filters default_files down to the files that are expandable.
187
186
  def expandable_default_files
188
187
  default_files.keep_if do |path|
189
- begin
190
- File.expand_path(path)
191
- true
192
- rescue ArgumentError
193
- false
194
- end
188
+ File.expand_path(path)
189
+ true
190
+ rescue ArgumentError
191
+ false
195
192
  end
196
193
  end
197
194
 
198
195
  private
199
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
+
200
217
  TRANSLATE_CONFIG_KEY_RENAME_MAP = {
201
218
  bindaddress: :bind_address,
202
219
  compression: :compression,
@@ -211,13 +228,14 @@ module Net
211
228
  identityfile: :keys,
212
229
  fingerprinthash: :fingerprint_hash,
213
230
  port: :port,
214
- stricthostkeychecking: :strict_host_key_checking,
215
231
  user: :user,
216
232
  userknownhostsfile: :user_known_hosts_file,
217
233
  checkhostip: :check_host_ip
218
234
  }.freeze
219
235
  def translate_config_key(hash, key, value, settings)
220
236
  case key
237
+ when :stricthostkeychecking
238
+ hash[:verify_host_key] = translate_verify_host_key(value)
221
239
  when :ciphers
222
240
  hash[:encryption] = value.split(/,/)
223
241
  when :hostbasedauthentication
@@ -235,12 +253,7 @@ module Net
235
253
  when :serveralivecountmax
236
254
  hash[:keepalive_maxcount] = value.to_i if value
237
255
  when :serveraliveinterval
238
- if value && value.to_i > 0
239
- hash[:keepalive] = true
240
- hash[:keepalive_interval] = value.to_i
241
- else
242
- hash[:keepalive] = false
243
- end
256
+ translate_keepalive(hash, value)
244
257
  when :passwordauthentication
245
258
  if value
246
259
  (hash[:auth_methods] << 'password').uniq!
@@ -302,9 +315,9 @@ module Net
302
315
  # host names.
303
316
  def pattern2regex(pattern)
304
317
  tail = pattern
305
- prefix = ""
318
+ prefix = String.new
306
319
  while !tail.empty? do
307
- head,sep,tail = tail.partition(/[\*\?]/)
320
+ head, sep, tail = tail.partition(/[\*\?]/)
308
321
  prefix = prefix + Regexp.quote(head)
309
322
  case sep
310
323
  when '*'
@@ -358,7 +371,7 @@ module Net
358
371
 
359
372
  conditions = conditions.each_slice(2)
360
373
  condition_matches = []
361
- conditions.each do |(kind,exprs)|
374
+ conditions.each do |(kind, exprs)|
362
375
  exprs = unquote(exprs)
363
376
 
364
377
  case kind.downcase