net-ssh 2.9.2 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.gitignore +6 -0
  4. data/.rubocop.yml +5 -0
  5. data/.rubocop_todo.yml +1129 -0
  6. data/.travis.yml +41 -5
  7. data/CHANGES.txt +133 -1
  8. data/Gemfile +13 -0
  9. data/Gemfile.norbnacl +10 -0
  10. data/Gemfile.norbnacl.lock +41 -0
  11. data/ISSUE_TEMPLATE.md +30 -0
  12. data/README.rdoc +26 -81
  13. data/Rakefile +63 -45
  14. data/appveyor.yml +51 -0
  15. data/lib/net/ssh/authentication/agent.rb +174 -14
  16. data/lib/net/ssh/authentication/ed25519.rb +137 -0
  17. data/lib/net/ssh/authentication/ed25519_loader.rb +21 -0
  18. data/lib/net/ssh/authentication/key_manager.rb +36 -30
  19. data/lib/net/ssh/authentication/methods/abstract.rb +4 -0
  20. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +16 -9
  21. data/lib/net/ssh/authentication/methods/password.rb +17 -4
  22. data/lib/net/ssh/authentication/pageant.rb +166 -45
  23. data/lib/net/ssh/authentication/session.rb +3 -2
  24. data/lib/net/ssh/buffer.rb +49 -10
  25. data/lib/net/ssh/buffered_io.rb +17 -12
  26. data/lib/net/ssh/config.rb +39 -8
  27. data/lib/net/ssh/connection/channel.rb +42 -20
  28. data/lib/net/ssh/connection/event_loop.rb +114 -0
  29. data/lib/net/ssh/connection/keepalive.rb +2 -2
  30. data/lib/net/ssh/connection/session.rb +120 -34
  31. data/lib/net/ssh/errors.rb +6 -6
  32. data/lib/net/ssh/key_factory.rb +49 -43
  33. data/lib/net/ssh/known_hosts.rb +49 -3
  34. data/lib/net/ssh/prompt.rb +47 -78
  35. data/lib/net/ssh/proxy/command.rb +31 -5
  36. data/lib/net/ssh/proxy/http.rb +15 -11
  37. data/lib/net/ssh/proxy/https.rb +49 -0
  38. data/lib/net/ssh/proxy/socks4.rb +2 -1
  39. data/lib/net/ssh/proxy/socks5.rb +3 -2
  40. data/lib/net/ssh/ruby_compat.rb +2 -29
  41. data/lib/net/ssh/service/forward.rb +2 -2
  42. data/lib/net/ssh/test/channel.rb +7 -0
  43. data/lib/net/ssh/test/extensions.rb +17 -0
  44. data/lib/net/ssh/test/kex.rb +4 -4
  45. data/lib/net/ssh/test/packet.rb +18 -2
  46. data/lib/net/ssh/test/script.rb +16 -2
  47. data/lib/net/ssh/test/socket.rb +1 -1
  48. data/lib/net/ssh/test.rb +5 -5
  49. data/lib/net/ssh/transport/algorithms.rb +92 -75
  50. data/lib/net/ssh/transport/cipher_factory.rb +19 -26
  51. data/lib/net/ssh/transport/ctr.rb +7 -9
  52. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +20 -9
  53. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +5 -3
  54. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +1 -1
  55. data/lib/net/ssh/transport/key_expander.rb +1 -0
  56. data/lib/net/ssh/transport/openssl.rb +1 -1
  57. data/lib/net/ssh/transport/packet_stream.rb +11 -3
  58. data/lib/net/ssh/transport/server_version.rb +13 -6
  59. data/lib/net/ssh/transport/session.rb +20 -10
  60. data/lib/net/ssh/transport/state.rb +1 -1
  61. data/lib/net/ssh/verifiers/secure.rb +8 -10
  62. data/lib/net/ssh/version.rb +4 -4
  63. data/lib/net/ssh.rb +62 -14
  64. data/net-ssh-public_cert.pem +19 -18
  65. data/net-ssh.gemspec +34 -194
  66. data/support/arcfour_check.rb +1 -1
  67. data/support/ssh_tunnel_bug.rb +1 -1
  68. data.tar.gz.sig +0 -0
  69. metadata +125 -109
  70. metadata.gz.sig +0 -0
  71. data/Rudyfile +0 -96
  72. data/lib/net/ssh/authentication/agent/java_pageant.rb +0 -85
  73. data/lib/net/ssh/authentication/agent/socket.rb +0 -178
  74. data/setup.rb +0 -1585
  75. data/test/README.txt +0 -47
  76. data/test/authentication/methods/common.rb +0 -28
  77. data/test/authentication/methods/test_abstract.rb +0 -51
  78. data/test/authentication/methods/test_hostbased.rb +0 -114
  79. data/test/authentication/methods/test_keyboard_interactive.rb +0 -100
  80. data/test/authentication/methods/test_none.rb +0 -41
  81. data/test/authentication/methods/test_password.rb +0 -95
  82. data/test/authentication/methods/test_publickey.rb +0 -148
  83. data/test/authentication/test_agent.rb +0 -224
  84. data/test/authentication/test_key_manager.rb +0 -227
  85. data/test/authentication/test_session.rb +0 -107
  86. data/test/common.rb +0 -108
  87. data/test/configs/auth_off +0 -5
  88. data/test/configs/auth_on +0 -4
  89. data/test/configs/empty +0 -0
  90. data/test/configs/eqsign +0 -3
  91. data/test/configs/exact_match +0 -8
  92. data/test/configs/host_plus +0 -10
  93. data/test/configs/multihost +0 -4
  94. data/test/configs/negative_match +0 -6
  95. data/test/configs/nohost +0 -19
  96. data/test/configs/numeric_host +0 -4
  97. data/test/configs/send_env +0 -2
  98. data/test/configs/substitutes +0 -8
  99. data/test/configs/wild_cards +0 -14
  100. data/test/connection/test_channel.rb +0 -467
  101. data/test/connection/test_session.rb +0 -543
  102. data/test/known_hosts/github +0 -1
  103. data/test/manual/test_forward.rb +0 -285
  104. data/test/manual/test_pageant.rb +0 -37
  105. data/test/start/test_connection.rb +0 -53
  106. data/test/start/test_options.rb +0 -43
  107. data/test/start/test_transport.rb +0 -28
  108. data/test/test_all.rb +0 -11
  109. data/test/test_buffer.rb +0 -433
  110. data/test/test_buffered_io.rb +0 -63
  111. data/test/test_config.rb +0 -221
  112. data/test/test_key_factory.rb +0 -191
  113. data/test/test_known_hosts.rb +0 -13
  114. data/test/transport/hmac/test_md5.rb +0 -41
  115. data/test/transport/hmac/test_md5_96.rb +0 -27
  116. data/test/transport/hmac/test_none.rb +0 -34
  117. data/test/transport/hmac/test_ripemd160.rb +0 -36
  118. data/test/transport/hmac/test_sha1.rb +0 -36
  119. data/test/transport/hmac/test_sha1_96.rb +0 -27
  120. data/test/transport/hmac/test_sha2_256.rb +0 -37
  121. data/test/transport/hmac/test_sha2_256_96.rb +0 -27
  122. data/test/transport/hmac/test_sha2_512.rb +0 -37
  123. data/test/transport/hmac/test_sha2_512_96.rb +0 -27
  124. data/test/transport/kex/test_diffie_hellman_group14_sha1.rb +0 -13
  125. data/test/transport/kex/test_diffie_hellman_group1_sha1.rb +0 -146
  126. data/test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb +0 -92
  127. data/test/transport/kex/test_diffie_hellman_group_exchange_sha256.rb +0 -34
  128. data/test/transport/kex/test_ecdh_sha2_nistp256.rb +0 -161
  129. data/test/transport/kex/test_ecdh_sha2_nistp384.rb +0 -38
  130. data/test/transport/kex/test_ecdh_sha2_nistp521.rb +0 -38
  131. data/test/transport/test_algorithms.rb +0 -324
  132. data/test/transport/test_cipher_factory.rb +0 -443
  133. data/test/transport/test_hmac.rb +0 -34
  134. data/test/transport/test_identity_cipher.rb +0 -40
  135. data/test/transport/test_packet_stream.rb +0 -1761
  136. data/test/transport/test_server_version.rb +0 -78
  137. data/test/transport/test_session.rb +0 -331
  138. data/test/transport/test_state.rb +0 -181
@@ -1,10 +1,22 @@
1
- require 'dl/import'
2
-
3
1
  if RUBY_VERSION < "1.9"
2
+ require 'dl/import'
4
3
  require 'dl/struct'
5
- else
4
+ elsif RUBY_VERSION < "2.1"
5
+ require 'dl/import'
6
6
  require 'dl/types'
7
7
  require 'dl'
8
+ else
9
+ require 'fiddle'
10
+ require 'fiddle/types'
11
+ require 'fiddle/import'
12
+
13
+ # For now map DL to Fiddler versus updating all the code below
14
+ module DL
15
+ CPtr ||= Fiddle::Pointer
16
+ if RUBY_PLATFORM != "java"
17
+ RUBY_FREE ||= Fiddle::RUBY_FREE
18
+ end
19
+ end
8
20
  end
9
21
 
10
22
  require 'net/ssh/errors'
@@ -26,7 +38,7 @@ module Net; module SSH; module Authentication
26
38
 
27
39
  # The definition of the Windows methods and data structures used in
28
40
  # communicating with the pageant process.
29
- module Win
41
+ module Win # rubocop:disable Metrics/ModuleLength
30
42
  # Compatibility on initialization
31
43
  if RUBY_VERSION < "1.9"
32
44
  extend DL::Importable
@@ -36,14 +48,24 @@ module Net; module SSH; module Authentication
36
48
  dlload 'advapi32'
37
49
 
38
50
  SIZEOF_DWORD = DL.sizeof('L')
39
- else
51
+ elsif RUBY_VERSION < "2.1"
40
52
  extend DL::Importer
41
53
  dlload 'user32','kernel32', 'advapi32'
42
54
  include DL::Win32Types
43
55
 
44
56
  SIZEOF_DWORD = DL::SIZEOF_LONG
57
+ else
58
+ extend Fiddle::Importer
59
+ dlload 'user32','kernel32', 'advapi32'
60
+ include Fiddle::Win32Types
61
+ SIZEOF_DWORD = Fiddle::SIZEOF_LONG
45
62
  end
46
63
 
64
+ if RUBY_ENGINE=="jruby"
65
+ typealias("HANDLE", "void *") # From winnt.h
66
+ typealias("PHANDLE", "void *") # From winnt.h
67
+ typealias("ULONG_PTR", "unsigned long*")
68
+ end
47
69
  typealias("LPCTSTR", "char *") # From winnt.h
48
70
  typealias("LPVOID", "void *") # From winnt.h
49
71
  typealias("LPCVOID", "const void *") # From windef.h
@@ -62,18 +84,24 @@ module Net; module SSH; module Authentication
62
84
 
63
85
  SMTO_NORMAL = 0 # From winuser.h
64
86
 
87
+ SUFFIX = if RUBY_ENGINE == "jruby"
88
+ "A"
89
+ else
90
+ ""
91
+ end
92
+
65
93
  # args: lpClassName, lpWindowName
66
- extern 'HWND FindWindow(LPCTSTR, LPCTSTR)'
94
+ extern "HWND FindWindow#{SUFFIX}(LPCTSTR, LPCTSTR)"
67
95
 
68
96
  # args: none
69
97
  extern 'DWORD GetCurrentThreadId()'
70
98
 
71
99
  # args: hFile, (ignored), flProtect, dwMaximumSizeHigh,
72
100
  # dwMaximumSizeLow, lpName
73
- extern 'HANDLE CreateFileMapping(HANDLE, void *, DWORD, ' +
74
- 'DWORD, DWORD, LPCTSTR)'
101
+ extern "HANDLE CreateFileMapping#{SUFFIX}(HANDLE, void *, DWORD, ' +
102
+ 'DWORD, DWORD, LPCTSTR)"
75
103
 
76
- # args: hFileMappingObject, dwDesiredAccess, dwFileOffsetHigh,
104
+ # args: hFileMappingObject, dwDesiredAccess, dwFileOffsetHigh,
77
105
  # dwfileOffsetLow, dwNumberOfBytesToMap
78
106
  extern 'LPVOID MapViewOfFile(HANDLE, DWORD, DWORD, DWORD, DWORD)'
79
107
 
@@ -84,9 +112,9 @@ module Net; module SSH; module Authentication
84
112
  extern 'BOOL CloseHandle(HANDLE)'
85
113
 
86
114
  # args: hWnd, Msg, wParam, lParam, fuFlags, uTimeout, lpdwResult
87
- extern 'LRESULT SendMessageTimeout(HWND, UINT, WPARAM, LPARAM, ' +
88
- 'UINT, UINT, PDWORD_PTR)'
89
-
115
+ extern "LRESULT SendMessageTimeout#{SUFFIX}(HWND, UINT, WPARAM, LPARAM, ' +
116
+ 'UINT, UINT, PDWORD_PTR)"
117
+
90
118
  # args: none
91
119
  extern 'DWORD GetLastError()'
92
120
 
@@ -112,8 +140,8 @@ module Net; module SSH; module Authentication
112
140
  extern 'BOOL IsValidSecurityDescriptor(LPVOID)'
113
141
 
114
142
  # Constants needed for security attribute retrieval.
115
- # Specifies the access mask corresponding to the desired access
116
- # rights.
143
+ # Specifies the access mask corresponding to the desired access
144
+ # rights.
117
145
  TOKEN_QUERY = 0x8
118
146
 
119
147
  # The value of TOKEN_USER from the TOKEN_INFORMATION_CLASS enum.
@@ -138,6 +166,13 @@ module Net; module SSH; module Authentication
138
166
  'LPVOID Group', 'LPVOID Sacl',
139
167
  'LPVOID Dacl']
140
168
 
169
+ # The COPYDATASTRUCT is used to send WM_COPYDATA messages
170
+ COPYDATASTRUCT = if RUBY_ENGINE == "jruby"
171
+ struct ['ULONG_PTR dwData', 'DWORD cbData', 'LPVOID lpData']
172
+ else
173
+ struct ['uintptr_t dwData', 'DWORD cbData', 'LPVOID lpData']
174
+ end
175
+
141
176
  # Compatibility for security attribute retrieval.
142
177
  if RUBY_VERSION < "1.9"
143
178
  # Alias functions to > 1.9 capitalization
@@ -171,6 +206,30 @@ module Net; module SSH; module Authentication
171
206
  def self.set_ptr_data(ptr, data)
172
207
  ptr[0] = data
173
208
  end
209
+ elsif RUBY_ENGINE == "jruby"
210
+ %w(FindWindow CreateFileMapping SendMessageTimeout).each do |name|
211
+ alias_method name, name+"A"
212
+ module_function name
213
+ end
214
+ # :nodoc:
215
+ module LibC
216
+ extend FFI::Library
217
+ ffi_lib FFI::Library::LIBC
218
+ attach_function :malloc, [:size_t], :pointer
219
+ attach_function :free, [:pointer], :void
220
+ end
221
+
222
+ def self.malloc_ptr(size)
223
+ Fiddle::Pointer.new(LibC.malloc(size), size, LibC.method(:free))
224
+ end
225
+
226
+ def self.get_ptr(ptr)
227
+ return data.address
228
+ end
229
+
230
+ def self.set_ptr_data(ptr, data)
231
+ ptr.write_string_length(data, data.size)
232
+ end
174
233
  else
175
234
  def self.malloc_ptr(size)
176
235
  return DL::CPtr.malloc(size, DL::RUBY_FREE)
@@ -191,22 +250,83 @@ module Net; module SSH; module Authentication
191
250
  psd_information = malloc_ptr(Win::SECURITY_DESCRIPTOR.size)
192
251
  raise_error_if_zero(
193
252
  Win.InitializeSecurityDescriptor(psd_information,
194
- Win::REVISION))
253
+ Win::REVISION)
254
+ )
195
255
  raise_error_if_zero(
196
- Win.SetSecurityDescriptorOwner(psd_information, user.SID,
197
- 0))
256
+ Win.SetSecurityDescriptorOwner(psd_information, get_sid_ptr(user),
257
+ 0)
258
+ )
198
259
  raise_error_if_zero(
199
- Win.IsValidSecurityDescriptor(psd_information))
260
+ Win.IsValidSecurityDescriptor(psd_information)
261
+ )
200
262
 
201
- nLength = Win::SECURITY_ATTRIBUTES.size
202
- lpSecurityDescriptor = psd_information
203
- bInheritHandle = 1
204
- sa = [nLength, lpSecurityDescriptor.to_i,
205
- bInheritHandle].pack("LLC")
263
+ sa = Win::SECURITY_ATTRIBUTES.new(to_struct_ptr(malloc_ptr(Win::SECURITY_ATTRIBUTES.size)))
264
+ sa.nLength = Win::SECURITY_ATTRIBUTES.size
265
+ sa.lpSecurityDescriptor = psd_information.to_i
266
+ sa.bInheritHandle = 1
206
267
 
207
268
  return sa
208
269
  end
209
270
 
271
+ if RUBY_ENGINE == "jruby"
272
+ def self.ptr_to_s(ptr, size)
273
+ ret = ptr.to_s(size)
274
+ ret << "\x00" while ret.size < size
275
+ ret
276
+ end
277
+
278
+ def self.ptr_to_handle(phandle)
279
+ phandle.ptr
280
+ end
281
+
282
+ def self.ptr_to_dword(ptr)
283
+ first = ptr.ptr.to_i
284
+ second = ptr_to_s(ptr,Win::SIZEOF_DWORD).unpack('L')[0]
285
+ raise "Error" unless first == second
286
+ first
287
+ end
288
+
289
+ def self.to_token_user(ptoken_information)
290
+ TOKEN_USER.new(ptoken_information.to_ptr)
291
+ end
292
+
293
+ def self.to_struct_ptr(ptr)
294
+ ptr.to_ptr
295
+ end
296
+
297
+ def self.get_sid(user)
298
+ ptr_to_s(user.to_ptr.ptr,Win::SIZEOF_DWORD).unpack('L')[0]
299
+ end
300
+
301
+ def self.get_sid_ptr(user)
302
+ user.to_ptr.ptr
303
+ end
304
+ else
305
+ def self.get_sid(user)
306
+ user.SID
307
+ end
308
+
309
+ def self.ptr_to_handle(phandle)
310
+ phandle.ptr.to_i
311
+ end
312
+
313
+ def self.to_struct_ptr(ptr)
314
+ ptr
315
+ end
316
+
317
+ def self.ptr_to_dword(ptr)
318
+ ptr.to_s(Win::SIZEOF_DWORD).unpack('L')[0]
319
+ end
320
+
321
+ def self.to_token_user(ptoken_information)
322
+ TOKEN_USER.new(ptoken_information)
323
+ end
324
+
325
+ def self.get_sid_ptr(user)
326
+ user.SID
327
+ end
328
+ end
329
+
210
330
  def self.get_current_user
211
331
  token_handle = open_process_token(Win.GetCurrentProcess,
212
332
  Win::TOKEN_QUERY)
@@ -220,9 +340,9 @@ module Net; module SSH; module Authentication
220
340
 
221
341
  raise_error_if_zero(
222
342
  Win.OpenProcessToken(process_handle, desired_access,
223
- ptoken_handle))
224
- token_handle = ptoken_handle.ptr.to_i
225
-
343
+ ptoken_handle)
344
+ )
345
+ token_handle = ptr_to_handle(ptoken_handle)
226
346
  return token_handle
227
347
  end
228
348
 
@@ -237,7 +357,7 @@ module Net; module SSH; module Authentication
237
357
  Win.GetTokenInformation(token_handle,
238
358
  token_information_class,
239
359
  Win::NULL, 0, preturn_length)
240
- ptoken_information = malloc_ptr(preturn_length.ptr.to_i)
360
+ ptoken_information = malloc_ptr(ptr_to_dword(preturn_length))
241
361
 
242
362
  # This call is going to write the requested information to
243
363
  # the memory location referenced by token_information.
@@ -246,9 +366,10 @@ module Net; module SSH; module Authentication
246
366
  token_information_class,
247
367
  ptoken_information,
248
368
  ptoken_information.size,
249
- preturn_length))
369
+ preturn_length)
370
+ )
250
371
 
251
- return TOKEN_USER.new(ptoken_information)
372
+ return to_token_user(ptoken_information)
252
373
  end
253
374
 
254
375
  def self.raise_error_if_zero(result)
@@ -271,19 +392,17 @@ module Net; module SSH; module Authentication
271
392
 
272
393
  private_class_method :new
273
394
 
274
- # The factory method for creating a new Socket instance. The location
275
- # parameter is ignored, and is only needed for compatibility with
276
- # the general Socket interface.
277
- def self.open(location=nil)
395
+ # The factory method for creating a new Socket instance.
396
+ def self.open
278
397
  new
279
398
  end
280
399
 
281
- # Create a new instance that communicates with the running pageant
400
+ # Create a new instance that communicates with the running pageant
282
401
  # instance. If no such instance is running, this will cause an error.
283
402
  def initialize
284
403
  @win = Win.FindWindow("Pageant", "Pageant")
285
404
 
286
- if @win == 0
405
+ if @win.to_i == 0
287
406
  raise Net::SSH::Exception,
288
407
  "pageant process not running"
289
408
  end
@@ -296,28 +415,27 @@ module Net; module SSH; module Authentication
296
415
  # the first.
297
416
  def send(data, *args)
298
417
  @input_buffer.append(data)
299
-
418
+
300
419
  ret = data.length
301
-
420
+
302
421
  while true
303
422
  return ret if @input_buffer.length < 4
304
423
  msg_length = @input_buffer.read_long + 4
305
424
  @input_buffer.reset!
306
-
425
+
307
426
  return ret if @input_buffer.length < msg_length
308
427
  msg = @input_buffer.read!(msg_length)
309
428
  @output_buffer.append(send_query(msg))
310
429
  end
311
430
  end
312
-
431
+
313
432
  # Reads +n+ bytes from the cached result of the last query. If +n+
314
433
  # is +nil+, returns all remaining data from the last query.
315
434
  def read(n = nil)
316
435
  @output_buffer.read(n)
317
436
  end
318
437
 
319
- def close
320
- end
438
+ def close; end
321
439
 
322
440
  # Packages the given query string and sends it to the pageant
323
441
  # process via the Windows messaging subsystem. The result is
@@ -341,7 +459,7 @@ module Net; module SSH; module Authentication
341
459
  "Creation of file mapping failed with error: #{Win.GetLastError}"
342
460
  end
343
461
 
344
- ptr = Win.MapViewOfFile(filemap, Win::FILE_MAP_WRITE, 0, 0,
462
+ ptr = Win.MapViewOfFile(filemap, Win::FILE_MAP_WRITE, 0, 0,
345
463
  0)
346
464
 
347
465
  if ptr.nil? || ptr.null?
@@ -350,10 +468,13 @@ module Net; module SSH; module Authentication
350
468
 
351
469
  Win.set_ptr_data(ptr, query)
352
470
 
353
- cds = Win.get_ptr [AGENT_COPYDATA_ID, mapname.size + 1,
354
- Win.get_cstr(mapname)].pack("LLp")
471
+ # using struct to achieve proper alignment and field size on 64-bit platform
472
+ cds = Win::COPYDATASTRUCT.new(Win.malloc_ptr(Win::COPYDATASTRUCT.size))
473
+ cds.dwData = AGENT_COPYDATA_ID
474
+ cds.cbData = mapname.size + 1
475
+ cds.lpData = Win.get_cstr(mapname)
355
476
  succ = Win.SendMessageTimeout(@win, Win::WM_COPYDATA, Win::NULL,
356
- cds, Win::SMTO_NORMAL, 5000, id)
477
+ cds.to_ptr, Win::SMTO_NORMAL, 5000, id)
357
478
 
358
479
  if succ > 0
359
480
  retlen = 4 + ptr.to_s(4).unpack("N")[0]
@@ -69,8 +69,9 @@ module Net; module SSH; module Authentication
69
69
  attempted << name
70
70
 
71
71
  debug { "trying #{name}" }
72
- begin
73
- method = Methods.const_get(name.split(/\W+/).map { |p| p.capitalize }.join).new(self, :key_manager => key_manager)
72
+ begin
73
+ auth_class = Methods.const_get(name.split(/\W+/).map { |p| p.capitalize }.join)
74
+ method = auth_class.new(self, key_manager: key_manager, password_prompt: options[:password_prompt])
74
75
  rescue NameError
75
76
  debug{"Mechanism #{name} was requested, but isn't a known type. Ignoring it."}
76
77
  next
@@ -1,6 +1,8 @@
1
1
  require 'net/ssh/ruby_compat'
2
2
  require 'net/ssh/transport/openssl'
3
3
 
4
+ require 'net/ssh/authentication/ed25519_loader'
5
+
4
6
  module Net; module SSH
5
7
 
6
8
  # Net::SSH::Buffer is a flexible class for building and parsing binary
@@ -34,6 +36,7 @@ module Net; module SSH
34
36
  # * :long => write a 4-byte integer (#write_long)
35
37
  # * :byte => write a single byte (#write_byte)
36
38
  # * :string => write a 4-byte length followed by character data (#write_string)
39
+ # * :mstring => same as string, but caller cannot resuse the string, avoids potential duplication (#write_moved)
37
40
  # * :bool => write a single byte, interpreted as a boolean (#write_bool)
38
41
  # * :bignum => write an SSH-encoded bignum (#write_bignum)
39
42
  # * :key => write an SSH-encoded key value (#write_key)
@@ -159,7 +162,7 @@ module Net; module SSH
159
162
  index = @content.index(pattern, @position) or return nil
160
163
  length = case pattern
161
164
  when String then pattern.length
162
- when Fixnum then 1
165
+ when Integer then 1
163
166
  when Regexp then $&.length
164
167
  end
165
168
  index && read(index+length)
@@ -182,7 +185,7 @@ module Net; module SSH
182
185
  consume!
183
186
  data
184
187
  end
185
-
188
+
186
189
  # Return the next 8 bytes as a 64-bit integer (in network byte order).
187
190
  # Returns nil if there are less than 8 bytes remaining to be read in the
188
191
  # buffer.
@@ -245,16 +248,32 @@ module Net; module SSH
245
248
  case type
246
249
  when /^ssh-dss(-cert-v01@openssh\.com)?$/
247
250
  key = OpenSSL::PKey::DSA.new
248
- key.p = read_bignum
249
- key.q = read_bignum
250
- key.g = read_bignum
251
- key.pub_key = read_bignum
251
+ if key.respond_to?(:set_pqg)
252
+ key.set_pqg(read_bignum, read_bignum, read_bignum)
253
+ else
254
+ key.p = read_bignum
255
+ key.q = read_bignum
256
+ key.g = read_bignum
257
+ end
258
+ if key.respond_to?(:set_key)
259
+ key.set_key(read_bignum, nil)
260
+ else
261
+ key.pub_key = read_bignum
262
+ end
252
263
 
253
264
  when /^ssh-rsa(-cert-v01@openssh\.com)?$/
254
265
  key = OpenSSL::PKey::RSA.new
255
- key.e = read_bignum
256
- key.n = read_bignum
257
-
266
+ if key.respond_to?(:set_key)
267
+ e = read_bignum
268
+ n = read_bignum
269
+ key.set_key(n, e, nil)
270
+ else
271
+ key.e = read_bignum
272
+ key.n = read_bignum
273
+ end
274
+ when /^ssh-ed25519$/
275
+ Net::SSH::Authentication::ED25519Loader.raiseUnlessLoaded("unsupported key type `#{type}'")
276
+ key = Net::SSH::Authentication::ED25519::PubKey.read_keyblob(self)
258
277
  when /^ecdsa\-sha2\-(\w*)$/
259
278
  unless defined?(OpenSSL::PKey::EC)
260
279
  raise NotImplementedError, "unsupported key type `#{type}'"
@@ -281,7 +300,14 @@ module Net; module SSH
281
300
  # Writes the given data literally into the string. Does not alter the
282
301
  # read position. Returns the buffer object.
283
302
  def write(*data)
284
- data.each { |datum| @content << datum }
303
+ data.each { |datum| @content << datum.dup.force_encoding('BINARY') }
304
+ self
305
+ end
306
+
307
+ # Optimized version of write where the caller gives up ownership of string
308
+ # to the method. This way we can mutate the string.
309
+ def write_moved(string)
310
+ @content << string.force_encoding('BINARY')
285
311
  self
286
312
  end
287
313
 
@@ -324,6 +350,19 @@ module Net; module SSH
324
350
  self
325
351
  end
326
352
 
353
+ # Writes each argument to the buffer as an SSH2-encoded string. Each
354
+ # string is prefixed by its length, encoded as a 4-byte long integer.
355
+ # Does not alter the read position. Returns the buffer object.
356
+ # Might alter arguments see write_moved
357
+ def write_mstring(*text)
358
+ text.each do |string|
359
+ s = string.to_s
360
+ write_long(s.bytesize)
361
+ write_moved(s)
362
+ end
363
+ self
364
+ end
365
+
327
366
  # Writes each argument to the buffer as a (C-style) boolean, with 1
328
367
  # meaning true, and 0 meaning false. Does not alter the read position.
329
368
  # Returns the buffer object.
@@ -66,6 +66,9 @@ module Net; module SSH
66
66
  debug { "read #{data.length} bytes" }
67
67
  input.append(data)
68
68
  return data.length
69
+ rescue EOFError => e
70
+ @input_errors << e
71
+ return 0
69
72
  end
70
73
 
71
74
  # Read up to +length+ bytes from the input buffer. If +length+ is nil,
@@ -143,21 +146,23 @@ module Net; module SSH
143
146
  # Module#include to add this module.
144
147
  def initialize_buffered_io
145
148
  @input = Net::SSH::Buffer.new
149
+ @input_errors = []
146
150
  @output = Net::SSH::Buffer.new
151
+ @output_errors = []
147
152
  end
148
153
  end
149
154
 
150
155
 
151
-
156
+
152
157
  # Fixes for two issues by Miklós Fazekas:
153
158
  #
154
- # * if client closes a forwarded connection, but the server is
159
+ # * if client closes a forwarded connection, but the server is
155
160
  # reading, net-ssh terminates with IOError socket closed.
156
- # * if client force closes (RST) a forwarded connection, but
161
+ # * if client force closes (RST) a forwarded connection, but
157
162
  # server is reading, net-ssh terminates with [an exception]
158
163
  #
159
- # See:
160
- #
164
+ # See:
165
+ #
161
166
  # http://net-ssh.lighthouseapp.com/projects/36253/tickets/7
162
167
  # http://github.com/net-ssh/net-ssh/tree/portfwfix
163
168
  #
@@ -168,24 +173,24 @@ module Net; module SSH
168
173
  rescue Errno::ECONNRESET => e
169
174
  debug { "connection was reset => shallowing exception:#{e}" }
170
175
  return 0
171
- rescue IOError => e
172
- if e.message =~ /closed/ then
176
+ rescue IOError => e
177
+ if e.message =~ /closed/ then
173
178
  debug { "connection was reset => shallowing exception:#{e}" }
174
179
  return 0
175
180
  else
176
181
  raise
177
- end
182
+ end
178
183
  end
179
184
  end
180
-
185
+
181
186
  def send_pending
182
187
  begin
183
- super
188
+ super
184
189
  rescue Errno::ECONNRESET => e
185
190
  debug { "connection was reset => shallowing exception:#{e}" }
186
191
  return 0
187
192
  rescue IOError => e
188
- if e.message =~ /closed/ then
193
+ if e.message =~ /closed/ then
189
194
  debug { "connection was reset => shallowing exception:#{e}" }
190
195
  return 0
191
196
  else
@@ -194,5 +199,5 @@ module Net; module SSH
194
199
  end
195
200
  end
196
201
  end
197
-
202
+
198
203
  end; end
@@ -59,7 +59,7 @@ module Net; module SSH
59
59
  # given +files+ (defaulting to the list of files returned by
60
60
  # #default_files), translates the resulting hash into the options
61
61
  # recognized by Net::SSH, and returns them.
62
- def for(host, files=default_files)
62
+ def for(host, files=expandable_default_files)
63
63
  translate(files.inject({}) { |settings, file|
64
64
  load(file, host, settings)
65
65
  })
@@ -144,7 +144,7 @@ module Net; module SSH
144
144
  def translate(settings)
145
145
  auth_methods = default_auth_methods.clone
146
146
  (auth_methods << 'challenge-response').uniq!
147
- ret = settings.inject({:auth_methods=>auth_methods}) do |hash, (key, value)|
147
+ ret = settings.inject({auth_methods: auth_methods}) do |hash, (key, value)|
148
148
  case key
149
149
  when 'bindaddress' then
150
150
  hash[:bind_address] = value
@@ -178,6 +178,15 @@ module Net; module SSH
178
178
  hash[:keys] = value
179
179
  when 'macs' then
180
180
  hash[:hmac] = value.split(/,/)
181
+ when 'serveralivecountmax'
182
+ hash[:keepalive_maxcount] = value.to_i if value
183
+ when 'serveraliveinterval'
184
+ if value && value.to_i > 0
185
+ hash[:keepalive] = true
186
+ hash[:keepalive_interval] = value.to_i
187
+ else
188
+ hash[:keepalive] = false
189
+ end
181
190
  when 'passwordauthentication'
182
191
  if value
183
192
  (hash[:auth_methods] << 'password').uniq!
@@ -230,14 +239,36 @@ module Net; module SSH
230
239
 
231
240
  private
232
241
 
242
+ def expandable_default_files
243
+ default_files.keep_if do |path|
244
+ begin
245
+ File.expand_path(path)
246
+ true
247
+ rescue ArgumentError
248
+ false
249
+ end
250
+ end
251
+ end
252
+
233
253
  # Converts an ssh_config pattern into a regex for matching against
234
254
  # host names.
235
255
  def pattern2regex(pattern)
236
- pattern = "^" + pattern.to_s.gsub(/\./, "\\.").
237
- gsub(/\?/, '.').
238
- gsub(/([+\/])/, '\\\\\\0').
239
- gsub(/\*/, '.*') + "$"
240
- Regexp.new(pattern, true)
256
+ tail = pattern
257
+ prefix = ""
258
+ while !tail.empty? do
259
+ head,sep,tail = tail.partition(/[\*\?]/)
260
+ prefix = prefix + Regexp.quote(head)
261
+ case sep
262
+ when '*'
263
+ prefix += '.*'
264
+ when '?'
265
+ prefix += '.'
266
+ when ''
267
+ else
268
+ fail "Unpexpcted sep:#{sep}"
269
+ end
270
+ end
271
+ Regexp.new("^" + prefix + "$", true)
241
272
  end
242
273
 
243
274
  # Converts the given size into an integer number of bytes.
@@ -249,7 +280,7 @@ module Net; module SSH
249
280
  else size.to_i
250
281
  end
251
282
  end
252
-
283
+
253
284
  def merge_challenge_response_with_keyboard_interactive(hash)
254
285
  if hash[:auth_methods].include?('challenge-response')
255
286
  hash[:auth_methods].delete('challenge-response')