net-ssh 5.2.0 → 7.0.1

Sign up to get free protection for your applications and to get access to all the features.
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