net-ssh 5.2.0 → 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 +5 -5
  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 +16 -2
  10. data/.rubocop_todo.yml +623 -511
  11. data/CHANGES.txt +50 -2
  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 +29 -13
  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 +13 -8
  25. data/lib/net/ssh/authentication/ed25519_loader.rb +5 -8
  26. data/lib/net/ssh/authentication/key_manager.rb +73 -32
  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 +56 -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 +51 -40
  37. data/lib/net/ssh/buffered_io.rb +24 -26
  38. data/lib/net/ssh/config.rb +82 -50
  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 +10 -13
  47. data/lib/net/ssh/known_hosts.rb +106 -39
  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 +35 -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 +7 -7
  68. data/lib/net/ssh/transport/algorithms.rb +100 -58
  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 -127
  102. data/lib/net/ssh/transport/packet_stream.rb +50 -16
  103. data/lib/net/ssh/transport/server_version.rb +17 -16
  104. data/lib/net/ssh/transport/session.rb +9 -7
  105. data/lib/net/ssh/transport/state.rb +44 -44
  106. data/lib/net/ssh/verifiers/accept_new.rb +0 -2
  107. data/lib/net/ssh/verifiers/accept_new_or_local_tunnel.rb +1 -2
  108. data/lib/net/ssh/verifiers/always.rb +6 -4
  109. data/lib/net/ssh/verifiers/never.rb +0 -2
  110. data/lib/net/ssh/version.rb +3 -3
  111. data/lib/net/ssh.rb +12 -8
  112. data/net-ssh-public_cert.pem +8 -8
  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 +55 -30
  117. metadata.gz.sig +0 -0
  118. data/.travis.yml +0 -53
  119. data/Gemfile.noed25519.lock +0 -41
  120. data/README.rdoc +0 -194
  121. data/lib/net/ssh/ruby_compat.rb +0 -13
  122. data/support/arcfour_check.rb +0 -20
@@ -11,7 +11,6 @@ require 'net/ssh/authentication/methods/keyboard_interactive'
11
11
  module Net
12
12
  module SSH
13
13
  module Authentication
14
-
15
14
  # Raised if the current authentication method is not allowed
16
15
  class DisallowedMethod < Net::SSH::Exception
17
16
  end
@@ -42,7 +41,7 @@ module Net
42
41
 
43
42
  # Instantiates a new Authentication::Session object over the given
44
43
  # transport layer abstraction.
45
- def initialize(transport, options={})
44
+ def initialize(transport, options = {})
46
45
  self.logger = transport.logger
47
46
  @transport = transport
48
47
 
@@ -55,7 +54,7 @@ module Net
55
54
  # Attempts to authenticate the given user, in preparation for the next
56
55
  # service request. Returns true if an authentication method succeeds in
57
56
  # authenticating the user, and false otherwise.
58
- def authenticate(next_service, username, password=nil)
57
+ def authenticate(next_service, username, password = nil)
59
58
  debug { "beginning authentication of `#{username}'" }
60
59
 
61
60
  transport.send_message(transport.service_request("ssh-userauth"))
@@ -63,28 +62,30 @@ module Net
63
62
 
64
63
  key_manager = KeyManager.new(logger, options)
65
64
  keys.each { |key| key_manager.add(key) } unless keys.empty?
65
+ keycerts.each { |keycert| key_manager.add_keycert(keycert) } unless keycerts.empty?
66
66
  key_data.each { |key2| key_manager.add_key_data(key2) } unless key_data.empty?
67
67
  default_keys.each { |key| key_manager.add(key) } unless options.key?(:keys) || options.key?(:key_data)
68
68
 
69
69
  attempted = []
70
70
 
71
71
  @auth_methods.each do |name|
72
+ next unless @allowed_auth_methods.include?(name)
73
+
74
+ attempted << name
75
+
76
+ debug { "trying #{name}" }
72
77
  begin
73
- next unless @allowed_auth_methods.include?(name)
74
- attempted << name
75
-
76
- debug { "trying #{name}" }
77
- begin
78
- auth_class = Methods.const_get(name.split(/\W+/).map { |p| p.capitalize }.join)
79
- method = auth_class.new(self, key_manager: key_manager, password_prompt: options[:password_prompt])
80
- rescue NameError
81
- debug {"Mechanism #{name} was requested, but isn't a known type. Ignoring it."}
82
- next
83
- end
84
-
85
- return true if method.authenticate(next_service, username, password)
86
- rescue Net::SSH::Authentication::DisallowedMethod
78
+ auth_class = Methods.const_get(name.split(/\W+/).map { |p| p.capitalize }.join)
79
+ method = auth_class.new(self,
80
+ key_manager: key_manager, password_prompt: options[:password_prompt],
81
+ pubkey_algorithms: options[:pubkey_algorithms] || nil)
82
+ rescue NameError
83
+ debug {"Mechanism #{name} was requested, but isn't a known type. Ignoring it."}
84
+ next
87
85
  end
86
+
87
+ return true if method.authenticate(next_service, username, password)
88
+ rescue Net::SSH::Authentication::DisallowedMethod
88
89
  end
89
90
 
90
91
  error { "all authorization methods failed (tried #{attempted.join(', ')})" }
@@ -128,6 +129,7 @@ module Net
128
129
  def expect_message(type)
129
130
  message = next_message
130
131
  raise Net::SSH::Exception, "expected #{type}, got #{message.type} (#{message})" unless message.type == type
132
+
131
133
  message
132
134
  end
133
135
 
@@ -136,12 +138,8 @@ module Net
136
138
  # Returns an array of paths to the key files usually defined
137
139
  # by system default.
138
140
  def default_keys
139
- if defined?(OpenSSL::PKey::EC)
140
- %w[~/.ssh/id_ed25519 ~/.ssh/id_rsa ~/.ssh/id_dsa ~/.ssh/id_ecdsa
141
- ~/.ssh2/id_ed25519 ~/.ssh2/id_rsa ~/.ssh2/id_dsa ~/.ssh2/id_ecdsa]
142
- else
143
- %w[~/.ssh/id_dsa ~/.ssh/id_rsa ~/.ssh2/id_dsa ~/.ssh2/id_rsa]
144
- end
141
+ %w[~/.ssh/id_ed25519 ~/.ssh/id_rsa ~/.ssh/id_dsa ~/.ssh/id_ecdsa
142
+ ~/.ssh2/id_ed25519 ~/.ssh2/id_rsa ~/.ssh2/id_dsa ~/.ssh2/id_ecdsa]
145
143
  end
146
144
 
147
145
  # Returns an array of paths to the key files that should be used when
@@ -150,6 +148,12 @@ module Net
150
148
  Array(options[:keys])
151
149
  end
152
150
 
151
+ # Returns an array of paths to the keycert files that should be used when
152
+ # attempting any key-based authentication mechanism.
153
+ def keycerts
154
+ Array(options[:keycerts])
155
+ end
156
+
153
157
  # Returns an array of the key data that should be used when
154
158
  # attempting any key-based authentication mechanism.
155
159
  def key_data
@@ -1,4 +1,3 @@
1
- require 'net/ssh/ruby_compat'
2
1
  require 'net/ssh/transport/openssl'
3
2
 
4
3
  require 'net/ssh/authentication/certificate'
@@ -6,7 +5,6 @@ require 'net/ssh/authentication/ed25519_loader'
6
5
 
7
6
  module Net
8
7
  module SSH
9
-
10
8
  # Net::SSH::Buffer is a flexible class for building and parsing binary
11
9
  # data packets. It provides a stream-like interface for sequentially
12
10
  # reading data items from the buffer, as well as a useful helper method
@@ -72,7 +70,7 @@ module Net
72
70
 
73
71
  # Creates a new buffer, initialized to the given content. The position
74
72
  # is initialized to the beginning of the buffer.
75
- def initialize(content="")
73
+ def initialize(content = String.new)
76
74
  @content = content.to_s
77
75
  @position = 0
78
76
  end
@@ -119,7 +117,7 @@ module Net
119
117
  # Resets the buffer, making it empty. Also, resets the read position to
120
118
  # 0.
121
119
  def clear!
122
- @content = ""
120
+ @content = String.new
123
121
  @position = 0
124
122
  end
125
123
 
@@ -130,12 +128,12 @@ module Net
130
128
  # would otherwise tend to grow without bound.
131
129
  #
132
130
  # Returns the buffer object itself.
133
- def consume!(n=position)
131
+ def consume!(n = position)
134
132
  if n >= length
135
133
  # optimize for a fairly common case
136
134
  clear!
137
135
  elsif n > 0
138
- @content = @content[n..-1] || ""
136
+ @content = @content[n..-1] || String.new
139
137
  @position -= n
140
138
  @position = 0 if @position < 0
141
139
  end
@@ -173,7 +171,7 @@ module Net
173
171
  # Reads and returns the next +count+ bytes from the buffer, starting from
174
172
  # the read position. If +count+ is +nil+, this will return all remaining
175
173
  # text in the buffer. This method will increment the pointer.
176
- def read(count=nil)
174
+ def read(count = nil)
177
175
  count ||= length
178
176
  count = length - @position if @position + count > length
179
177
  @position += count
@@ -182,7 +180,7 @@ module Net
182
180
 
183
181
  # Reads (as #read) and returns the given number of bytes from the buffer,
184
182
  # and then consumes (as #consume!) all data up to the new read position.
185
- def read!(count=nil)
183
+ def read!(count = nil)
186
184
  data = read(count)
187
185
  consume!
188
186
  data
@@ -238,6 +236,7 @@ module Net
238
236
  def read_bignum
239
237
  data = read_string
240
238
  return unless data
239
+
241
240
  OpenSSL::BN.new(data, 2)
242
241
  end
243
242
 
@@ -284,6 +283,8 @@ module Net
284
283
  key.iqmp = iqmp
285
284
  end
286
285
  key
286
+ when /^ecdsa\-sha2\-(\w*)$/
287
+ OpenSSL::PKey::EC.read_keyblob($1, self)
287
288
  else
288
289
  raise Exception, "Cannot decode private key of type #{type}"
289
290
  end
@@ -296,42 +297,47 @@ module Net
296
297
  when /^(.*)-cert-v01@openssh\.com$/
297
298
  key = Net::SSH::Authentication::Certificate.read_certblob(self, $1)
298
299
  when /^ssh-dss$/
299
- key = OpenSSL::PKey::DSA.new
300
- if key.respond_to?(:set_pqg)
301
- key.set_pqg(read_bignum, read_bignum, read_bignum)
302
- else
303
- key.p = read_bignum
304
- key.q = read_bignum
305
- key.g = read_bignum
306
- end
307
- if key.respond_to?(:set_key)
308
- key.set_key(read_bignum, nil)
309
- else
310
- key.pub_key = read_bignum
311
- 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)
312
324
  when /^ssh-rsa$/
313
- key = OpenSSL::PKey::RSA.new
314
- if key.respond_to?(:set_key)
315
- e = read_bignum
316
- n = read_bignum
317
- key.set_key(n, e, nil)
318
- else
319
- key.e = read_bignum
320
- key.n = read_bignum
321
- 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)
322
336
  when /^ssh-ed25519$/
323
337
  Net::SSH::Authentication::ED25519Loader.raiseUnlessLoaded("unsupported key type `#{type}'")
324
338
  key = Net::SSH::Authentication::ED25519::PubKey.read_keyblob(self)
325
339
  when /^ecdsa\-sha2\-(\w*)$/
326
- unless defined?(OpenSSL::PKey::EC)
327
- raise NotImplementedError, "unsupported key type `#{type}'"
328
- else
329
- begin
330
- key = OpenSSL::PKey::EC.read_keyblob($1, self)
331
- rescue OpenSSL::PKey::ECError
332
- raise NotImplementedError, "unsupported key type `#{type}'"
333
- end
334
- end
340
+ key = OpenSSL::PKey::EC.read_keyblob($1, self)
335
341
  else
336
342
  raise NotImplementedError, "unsupported key type `#{type}'"
337
343
  end
@@ -355,7 +361,12 @@ module Net
355
361
  # Optimized version of write where the caller gives up ownership of string
356
362
  # to the method. This way we can mutate the string.
357
363
  def write_moved(string)
358
- @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
359
370
  self
360
371
  end
361
372
 
@@ -1,10 +1,8 @@
1
1
  require 'net/ssh/buffer'
2
2
  require 'net/ssh/loggable'
3
- require 'net/ssh/ruby_compat'
4
3
 
5
- module Net
4
+ module Net
6
5
  module SSH
7
-
8
6
  # This module is used to extend sockets and other IO objects, to allow
9
7
  # them to be buffered for both read and write. This abstraction makes it
10
8
  # quite easy to write a select-based event loop
@@ -49,19 +47,19 @@ module Net
49
47
  # end
50
48
  module BufferedIo
51
49
  include Loggable
52
-
50
+
53
51
  # Called when the #extend is called on an object, with this module as the
54
52
  # argument. It ensures that the modules instance variables are all properly
55
53
  # initialized.
56
- def self.extended(object) #:nodoc:
54
+ def self.extended(object) # :nodoc:
57
55
  # need to use __send__ because #send is overridden in Socket
58
56
  object.__send__(:initialize_buffered_io)
59
57
  end
60
-
58
+
61
59
  # Tries to read up to +n+ bytes of data from the remote end, and appends
62
60
  # the data to the input buffer. It returns the number of bytes read, or 0
63
61
  # if no data was available to be read.
64
- def fill(n=8192)
62
+ def fill(n = 8192)
65
63
  input.consume!
66
64
  data = recv(n)
67
65
  debug { "read #{data.length} bytes" }
@@ -71,31 +69,31 @@ module Net
71
69
  @input_errors << e
72
70
  return 0
73
71
  end
74
-
72
+
75
73
  # Read up to +length+ bytes from the input buffer. If +length+ is nil,
76
74
  # all available data is read from the buffer. (See #available.)
77
- def read_available(length=nil)
75
+ def read_available(length = nil)
78
76
  input.read(length || available)
79
77
  end
80
-
78
+
81
79
  # Returns the number of bytes available to be read from the input buffer.
82
80
  # (See #read_available.)
83
81
  def available
84
82
  input.available
85
83
  end
86
-
84
+
87
85
  # Enqueues data in the output buffer, to be written when #send_pending
88
86
  # is called. Note that the data is _not_ sent immediately by this method!
89
87
  def enqueue(data)
90
88
  output.append(data)
91
89
  end
92
-
90
+
93
91
  # Returns +true+ if there is data waiting in the output buffer, and
94
92
  # +false+ otherwise.
95
93
  def pending_write?
96
94
  output.length > 0
97
95
  end
98
-
96
+
99
97
  # Sends as much of the pending output as possible. Returns +true+ if any
100
98
  # data was sent, and +false+ otherwise.
101
99
  def send_pending
@@ -108,7 +106,7 @@ module Net
108
106
  return false
109
107
  end
110
108
  end
111
-
109
+
112
110
  # Calls #send_pending repeatedly, if necessary, blocking until the output
113
111
  # buffer is empty.
114
112
  def wait_for_pending_sends
@@ -116,31 +114,32 @@ module Net
116
114
  while output.length > 0
117
115
  result = IO.select(nil, [self]) or next
118
116
  next unless result[1].any?
117
+
119
118
  send_pending
120
119
  end
121
120
  end
122
-
121
+
123
122
  public # these methods are primarily for use in tests
124
-
125
- def write_buffer #:nodoc:
123
+
124
+ def write_buffer # :nodoc:
126
125
  output.to_s
127
126
  end
128
-
129
- def read_buffer #:nodoc:
127
+
128
+ def read_buffer # :nodoc:
130
129
  input.to_s
131
130
  end
132
-
131
+
133
132
  private
134
-
133
+
135
134
  #--
136
135
  # Can't use attr_reader here (after +private+) without incurring the
137
136
  # wrath of "ruby -w". We hates it.
138
137
  #++
139
-
138
+
140
139
  def input; @input; end
141
140
 
142
141
  def output; @output; end
143
-
142
+
144
143
  # Initializes the intput and output buffers for this object. This method
145
144
  # is called automatically when the module is mixed into an object via
146
145
  # Object#extend (see Net::SSH::BufferedIo.extended), but must be called
@@ -167,7 +166,7 @@ module Net
167
166
  # http://github.com/net-ssh/net-ssh/tree/portfwfix
168
167
  #
169
168
  module ForwardedBufferedIo
170
- def fill(n=8192)
169
+ def fill(n = 8192)
171
170
  begin
172
171
  super(n)
173
172
  rescue Errno::ECONNRESET => e
@@ -182,7 +181,7 @@ module Net
182
181
  end
183
182
  end
184
183
  end
185
-
184
+
186
185
  def send_pending
187
186
  begin
188
187
  super
@@ -199,6 +198,5 @@ module Net
199
198
  end
200
199
  end
201
200
  end
202
-
203
201
  end
204
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
@@ -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
@@ -33,6 +33,7 @@ module Net
33
33
  # * ProxyJump => maps to the :proxy option
34
34
  # * PubKeyAuthentication => maps to the :auth_methods option
35
35
  # * RekeyLimit => :rekey_limit
36
+ # * StrictHostKeyChecking => :verify_host_key
36
37
  # * User => :user
37
38
  # * UserKnownHostsFile => :user_known_hosts_file
38
39
  # * NumberOfPasswordPrompts => :number_of_password_prompts
@@ -64,7 +65,7 @@ module Net
64
65
  # given +files+ (defaulting to the list of files returned by
65
66
  # #default_files), translates the resulting hash into the options
66
67
  # recognized by Net::SSH, and returns them.
67
- def for(host, files=expandable_default_files)
68
+ def for(host, files = expandable_default_files)
68
69
  translate(files.inject({}) { |settings, file|
69
70
  load(file, host, settings)
70
71
  })
@@ -76,7 +77,7 @@ module Net
76
77
  # ones. Returns a hash containing the OpenSSH options. (See
77
78
  # #translate for how to convert the OpenSSH options into Net::SSH
78
79
  # options.)
79
- def load(path, host, settings={}, base_dir = nil)
80
+ def load(path, host, settings = {}, base_dir = nil)
80
81
  file = File.expand_path(path)
81
82
  base_dir ||= File.dirname(file)
82
83
  return settings unless File.readable?(file)
@@ -128,7 +129,7 @@ module Net
128
129
  block_seen = true
129
130
  elsif !block_seen
130
131
  case key
131
- when 'identityfile'
132
+ when 'identityfile', 'certificatefile'
132
133
  (globals[key] ||= []) << value
133
134
  when 'include'
134
135
  included_file_paths(base_dir, value).each do |file_path|
@@ -139,7 +140,7 @@ module Net
139
140
  end
140
141
  elsif block_matched
141
142
  case key
142
- when 'identityfile'
143
+ when 'identityfile', 'certificatefile'
143
144
  (settings[key] ||= []) << value
144
145
  when 'include'
145
146
  included_file_paths(base_dir, value).each do |file_path|
@@ -149,11 +150,18 @@ module Net
149
150
  settings[key] = value unless settings.key?(key)
150
151
  end
151
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
152
160
  end
153
161
 
154
162
  globals.merge(settings) do |key, oldval, newval|
155
163
  case key
156
- when 'identityfile'
164
+ when 'identityfile', 'certificatefile'
157
165
  oldval + newval
158
166
  else
159
167
  newval
@@ -177,36 +185,57 @@ module Net
177
185
  # Filters default_files down to the files that are expandable.
178
186
  def expandable_default_files
179
187
  default_files.keep_if do |path|
180
- begin
181
- File.expand_path(path)
182
- true
183
- rescue ArgumentError
184
- false
185
- end
188
+ File.expand_path(path)
189
+ true
190
+ rescue ArgumentError
191
+ false
186
192
  end
187
193
  end
188
194
 
189
195
  private
190
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
191
235
  def translate_config_key(hash, key, value, settings)
192
- rename = {
193
- bindaddress: :bind_address,
194
- compression: :compression,
195
- compressionlevel: :compression_level,
196
- connecttimeout: :timeout,
197
- forwardagent: :forward_agent,
198
- identitiesonly: :keys_only,
199
- identityagent: :identity_agent,
200
- globalknownhostsfile: :global_known_hosts_file,
201
- hostkeyalias: :host_key_alias,
202
- identityfile: :keys,
203
- fingerprinthash: :fingerprint_hash,
204
- port: :port,
205
- user: :user,
206
- userknownhostsfile: :user_known_hosts_file,
207
- checkhostip: :check_host_ip
208
- }
209
236
  case key
237
+ when :stricthostkeychecking
238
+ hash[:verify_host_key] = translate_verify_host_key(value)
210
239
  when :ciphers
211
240
  hash[:encryption] = value.split(/,/)
212
241
  when :hostbasedauthentication
@@ -224,12 +253,7 @@ module Net
224
253
  when :serveralivecountmax
225
254
  hash[:keepalive_maxcount] = value.to_i if value
226
255
  when :serveraliveinterval
227
- if value && value.to_i > 0
228
- hash[:keepalive] = true
229
- hash[:keepalive_interval] = value.to_i
230
- else
231
- hash[:keepalive] = false
232
- end
256
+ translate_keepalive(hash, value)
233
257
  when :passwordauthentication
234
258
  if value
235
259
  (hash[:auth_methods] << 'password').uniq!
@@ -250,15 +274,9 @@ module Net
250
274
  end
251
275
  when :preferredauthentications
252
276
  hash[:auth_methods] = value.split(/,/) # TODO we should place to preferred_auth_methods rather than auth_methods
253
- when :proxycommand
254
- if value and value !~ /^none$/
255
- require 'net/ssh/proxy/command'
256
- hash[:proxy] = Net::SSH::Proxy::Command.new(value)
257
- end
258
- when :proxyjump
259
- if value
260
- require 'net/ssh/proxy/jump'
261
- hash[:proxy] = Net::SSH::Proxy::Jump.new(value)
277
+ when :proxy
278
+ if (proxy = setup_proxy(*value))
279
+ hash[:proxy] = proxy
262
280
  end
263
281
  when :pubkeyauthentication
264
282
  if value
@@ -271,10 +289,25 @@ module Net
271
289
  when :sendenv
272
290
  multi_send_env = value.to_s.split(/\s+/)
273
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
274
294
  when :numberofpasswordprompts
275
295
  hash[:number_of_password_prompts] = value.to_i
276
- when *rename.keys
277
- 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)
278
311
  end
279
312
  end
280
313
 
@@ -282,9 +315,9 @@ module Net
282
315
  # host names.
283
316
  def pattern2regex(pattern)
284
317
  tail = pattern
285
- prefix = ""
318
+ prefix = String.new
286
319
  while !tail.empty? do
287
- head,sep,tail = tail.partition(/[\*\?]/)
320
+ head, sep, tail = tail.partition(/[\*\?]/)
288
321
  prefix = prefix + Regexp.quote(head)
289
322
  case sep
290
323
  when '*'
@@ -338,7 +371,7 @@ module Net
338
371
 
339
372
  conditions = conditions.each_slice(2)
340
373
  condition_matches = []
341
- conditions.each do |(kind,exprs)|
374
+ conditions.each do |(kind, exprs)|
342
375
  exprs = unquote(exprs)
343
376
 
344
377
  case kind.downcase
@@ -369,6 +402,5 @@ module Net
369
402
  end
370
403
  end
371
404
  end
372
-
373
405
  end
374
406
  end