net-ssh 6.1.0 → 7.3.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 (116) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.dockerignore +6 -0
  4. data/.github/FUNDING.yml +1 -0
  5. data/.github/config/rubocop_linter_action.yml +4 -0
  6. data/.github/workflows/ci-with-docker.yml +44 -0
  7. data/.github/workflows/ci.yml +94 -0
  8. data/.github/workflows/rubocop.yml +16 -0
  9. data/.gitignore +4 -0
  10. data/.rubocop.yml +12 -1
  11. data/.rubocop_todo.yml +475 -376
  12. data/CHANGES.txt +64 -3
  13. data/DEVELOPMENT.md +23 -0
  14. data/Dockerfile +29 -0
  15. data/Dockerfile.openssl3 +17 -0
  16. data/Gemfile +2 -0
  17. data/Gemfile.noed25519 +2 -0
  18. data/Gemfile.norbnacl +12 -0
  19. data/README.md +38 -22
  20. data/Rakefile +92 -0
  21. data/SECURITY.md +4 -0
  22. data/docker-compose.yml +25 -0
  23. data/lib/net/ssh/authentication/agent.rb +29 -13
  24. data/lib/net/ssh/authentication/certificate.rb +14 -11
  25. data/lib/net/ssh/authentication/constants.rb +0 -1
  26. data/lib/net/ssh/authentication/ed25519.rb +14 -11
  27. data/lib/net/ssh/authentication/ed25519_loader.rb +4 -7
  28. data/lib/net/ssh/authentication/key_manager.rb +65 -36
  29. data/lib/net/ssh/authentication/methods/abstract.rb +12 -3
  30. data/lib/net/ssh/authentication/methods/hostbased.rb +3 -5
  31. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +2 -2
  32. data/lib/net/ssh/authentication/methods/none.rb +6 -9
  33. data/lib/net/ssh/authentication/methods/password.rb +2 -3
  34. data/lib/net/ssh/authentication/methods/publickey.rb +57 -17
  35. data/lib/net/ssh/authentication/pageant.rb +97 -97
  36. data/lib/net/ssh/authentication/pub_key_fingerprint.rb +3 -3
  37. data/lib/net/ssh/authentication/session.rb +25 -17
  38. data/lib/net/ssh/buffer.rb +71 -51
  39. data/lib/net/ssh/buffered_io.rb +25 -26
  40. data/lib/net/ssh/config.rb +33 -20
  41. data/lib/net/ssh/connection/channel.rb +84 -82
  42. data/lib/net/ssh/connection/constants.rb +0 -4
  43. data/lib/net/ssh/connection/event_loop.rb +30 -24
  44. data/lib/net/ssh/connection/keepalive.rb +12 -12
  45. data/lib/net/ssh/connection/session.rb +109 -108
  46. data/lib/net/ssh/connection/term.rb +56 -58
  47. data/lib/net/ssh/errors.rb +12 -12
  48. data/lib/net/ssh/key_factory.rb +7 -8
  49. data/lib/net/ssh/known_hosts.rb +86 -18
  50. data/lib/net/ssh/loggable.rb +8 -9
  51. data/lib/net/ssh/packet.rb +1 -1
  52. data/lib/net/ssh/prompt.rb +9 -11
  53. data/lib/net/ssh/proxy/command.rb +1 -1
  54. data/lib/net/ssh/proxy/errors.rb +2 -4
  55. data/lib/net/ssh/proxy/http.rb +18 -20
  56. data/lib/net/ssh/proxy/https.rb +8 -10
  57. data/lib/net/ssh/proxy/jump.rb +8 -10
  58. data/lib/net/ssh/proxy/socks4.rb +2 -4
  59. data/lib/net/ssh/proxy/socks5.rb +3 -5
  60. data/lib/net/ssh/service/forward.rb +7 -7
  61. data/lib/net/ssh/test/channel.rb +24 -26
  62. data/lib/net/ssh/test/extensions.rb +35 -35
  63. data/lib/net/ssh/test/kex.rb +6 -8
  64. data/lib/net/ssh/test/local_packet.rb +0 -2
  65. data/lib/net/ssh/test/packet.rb +3 -3
  66. data/lib/net/ssh/test/remote_packet.rb +6 -8
  67. data/lib/net/ssh/test/script.rb +25 -27
  68. data/lib/net/ssh/test/socket.rb +12 -15
  69. data/lib/net/ssh/test.rb +4 -5
  70. data/lib/net/ssh/transport/aes128_gcm.rb +40 -0
  71. data/lib/net/ssh/transport/aes256_gcm.rb +40 -0
  72. data/lib/net/ssh/transport/algorithms.rb +51 -19
  73. data/lib/net/ssh/transport/chacha20_poly1305_cipher.rb +117 -0
  74. data/lib/net/ssh/transport/chacha20_poly1305_cipher_loader.rb +17 -0
  75. data/lib/net/ssh/transport/cipher_factory.rb +56 -29
  76. data/lib/net/ssh/transport/constants.rb +3 -3
  77. data/lib/net/ssh/transport/ctr.rb +7 -7
  78. data/lib/net/ssh/transport/gcm_cipher.rb +207 -0
  79. data/lib/net/ssh/transport/hmac/abstract.rb +20 -5
  80. data/lib/net/ssh/transport/hmac/md5.rb +0 -2
  81. data/lib/net/ssh/transport/hmac/md5_96.rb +0 -2
  82. data/lib/net/ssh/transport/hmac/none.rb +0 -2
  83. data/lib/net/ssh/transport/hmac/ripemd160.rb +0 -2
  84. data/lib/net/ssh/transport/hmac/sha1.rb +0 -2
  85. data/lib/net/ssh/transport/hmac/sha1_96.rb +0 -2
  86. data/lib/net/ssh/transport/hmac.rb +12 -12
  87. data/lib/net/ssh/transport/identity_cipher.rb +19 -13
  88. data/lib/net/ssh/transport/kex/abstract.rb +12 -5
  89. data/lib/net/ssh/transport/kex/abstract5656.rb +1 -1
  90. data/lib/net/ssh/transport/kex/curve25519_sha256.rb +2 -1
  91. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +4 -4
  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 +21 -21
  94. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +1 -2
  95. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +2 -2
  96. data/lib/net/ssh/transport/kex.rb +8 -6
  97. data/lib/net/ssh/transport/key_expander.rb +7 -8
  98. data/lib/net/ssh/transport/openssl.rb +51 -26
  99. data/lib/net/ssh/transport/openssl_cipher_extensions.rb +8 -0
  100. data/lib/net/ssh/transport/packet_stream.rb +46 -26
  101. data/lib/net/ssh/transport/server_version.rb +17 -16
  102. data/lib/net/ssh/transport/session.rb +9 -7
  103. data/lib/net/ssh/transport/state.rb +44 -44
  104. data/lib/net/ssh/verifiers/accept_new.rb +0 -2
  105. data/lib/net/ssh/verifiers/accept_new_or_local_tunnel.rb +1 -2
  106. data/lib/net/ssh/verifiers/always.rb +6 -4
  107. data/lib/net/ssh/verifiers/never.rb +0 -2
  108. data/lib/net/ssh/version.rb +2 -2
  109. data/lib/net/ssh.rb +15 -8
  110. data/net-ssh-public_cert.pem +19 -18
  111. data/net-ssh.gemspec +7 -4
  112. data/support/ssh_tunnel_bug.rb +3 -3
  113. data.tar.gz.sig +0 -0
  114. metadata +76 -29
  115. metadata.gz.sig +0 -0
  116. data/.travis.yml +0 -52
@@ -21,10 +21,9 @@ end
21
21
 
22
22
  require 'net/ssh/errors'
23
23
 
24
- module Net
25
- module SSH
24
+ module Net
25
+ module SSH
26
26
  module Authentication
27
-
28
27
  # This module encapsulates the implementation of a socket factory that
29
28
  # uses the PuTTY "pageant" utility to obtain information about SSH
30
29
  # identities.
@@ -33,36 +32,35 @@ module Net
33
32
  # by Guillaume Marçais (guillaume.marcais@free.fr). It is used and
34
33
  # relicensed by permission.
35
34
  module Pageant
36
-
37
35
  # From Putty pageant.c
38
36
  AGENT_MAX_MSGLEN = 8192
39
37
  AGENT_COPYDATA_ID = 0x804e50ba
40
-
38
+
41
39
  # The definition of the Windows methods and data structures used in
42
40
  # communicating with the pageant process.
43
41
  module Win # rubocop:disable Metrics/ModuleLength
44
42
  # Compatibility on initialization
45
43
  if RUBY_VERSION < "1.9"
46
44
  extend DL::Importable
47
-
45
+
48
46
  dlload 'user32.dll'
49
47
  dlload 'kernel32.dll'
50
48
  dlload 'advapi32.dll'
51
-
49
+
52
50
  SIZEOF_DWORD = DL.sizeof('L')
53
51
  elsif RUBY_VERSION < "2.1"
54
52
  extend DL::Importer
55
- dlload 'user32.dll','kernel32.dll', 'advapi32.dll'
53
+ dlload 'user32.dll', 'kernel32.dll', 'advapi32.dll'
56
54
  include DL::Win32Types
57
-
55
+
58
56
  SIZEOF_DWORD = DL::SIZEOF_LONG
59
57
  else
60
58
  extend Fiddle::Importer
61
- dlload 'user32.dll','kernel32.dll', 'advapi32.dll'
59
+ dlload 'user32.dll', 'kernel32.dll', 'advapi32.dll'
62
60
  include Fiddle::Win32Types
63
61
  SIZEOF_DWORD = Fiddle::SIZEOF_LONG
64
62
  end
65
-
63
+
66
64
  if RUBY_ENGINE == "jruby"
67
65
  typealias("HANDLE", "void *") # From winnt.h
68
66
  typealias("PHANDLE", "void *") # From winnt.h
@@ -76,105 +74,105 @@ module Net
76
74
  typealias("LPARAM", "long *") # From windef.h
77
75
  typealias("PDWORD_PTR", "long *") # From basetsd.h
78
76
  typealias("USHORT", "unsigned short") # From windef.h
79
-
77
+
80
78
  # From winbase.h, winnt.h
81
79
  INVALID_HANDLE_VALUE = -1
82
80
  NULL = nil
83
81
  PAGE_READWRITE = 0x0004
84
82
  FILE_MAP_WRITE = 2
85
83
  WM_COPYDATA = 74
86
-
84
+
87
85
  SMTO_NORMAL = 0 # From winuser.h
88
-
86
+
89
87
  SUFFIX = if RUBY_ENGINE == "jruby"
90
88
  "A"
91
89
  else
92
90
  ""
93
91
  end
94
-
92
+
95
93
  # args: lpClassName, lpWindowName
96
94
  extern "HWND FindWindow#{SUFFIX}(LPCTSTR, LPCTSTR)"
97
-
95
+
98
96
  # args: none
99
97
  extern 'DWORD GetCurrentThreadId()'
100
-
98
+
101
99
  # args: hFile, (ignored), flProtect, dwMaximumSizeHigh,
102
100
  # dwMaximumSizeLow, lpName
103
101
  extern "HANDLE CreateFileMapping#{SUFFIX}(HANDLE, void *, DWORD, " +
104
102
  "DWORD, DWORD, LPCTSTR)"
105
-
103
+
106
104
  # args: hFileMappingObject, dwDesiredAccess, dwFileOffsetHigh,
107
105
  # dwfileOffsetLow, dwNumberOfBytesToMap
108
106
  extern 'LPVOID MapViewOfFile(HANDLE, DWORD, DWORD, DWORD, DWORD)'
109
-
107
+
110
108
  # args: lpBaseAddress
111
109
  extern 'BOOL UnmapViewOfFile(LPCVOID)'
112
-
110
+
113
111
  # args: hObject
114
112
  extern 'BOOL CloseHandle(HANDLE)'
115
-
113
+
116
114
  # args: hWnd, Msg, wParam, lParam, fuFlags, uTimeout, lpdwResult
117
115
  extern "LRESULT SendMessageTimeout#{SUFFIX}(HWND, UINT, WPARAM, LPARAM, " +
118
116
  "UINT, UINT, PDWORD_PTR)"
119
-
117
+
120
118
  # args: none
121
119
  extern 'DWORD GetLastError()'
122
-
120
+
123
121
  # args: none
124
122
  extern 'HANDLE GetCurrentProcess()'
125
-
123
+
126
124
  # args: hProcessHandle, dwDesiredAccess, (out) phNewTokenHandle
127
125
  extern 'BOOL OpenProcessToken(HANDLE, DWORD, PHANDLE)'
128
-
126
+
129
127
  # args: hTokenHandle, uTokenInformationClass,
130
128
  # (out) lpTokenInformation, dwTokenInformationLength
131
129
  # (out) pdwInfoReturnLength
132
130
  extern 'BOOL GetTokenInformation(HANDLE, UINT, LPVOID, DWORD, ' +
133
131
  'PDWORD)'
134
-
132
+
135
133
  # args: (out) lpSecurityDescriptor, dwRevisionLevel
136
134
  extern 'BOOL InitializeSecurityDescriptor(LPVOID, DWORD)'
137
-
135
+
138
136
  # args: (out) lpSecurityDescriptor, lpOwnerSid, bOwnerDefaulted
139
137
  extern 'BOOL SetSecurityDescriptorOwner(LPVOID, LPVOID, BOOL)'
140
-
138
+
141
139
  # args: pSecurityDescriptor
142
140
  extern 'BOOL IsValidSecurityDescriptor(LPVOID)'
143
-
141
+
144
142
  # Constants needed for security attribute retrieval.
145
143
  # Specifies the access mask corresponding to the desired access
146
144
  # rights.
147
145
  TOKEN_QUERY = 0x8
148
-
146
+
149
147
  # The value of TOKEN_USER from the TOKEN_INFORMATION_CLASS enum.
150
148
  TOKEN_USER_INFORMATION_CLASS = 1
151
-
149
+
152
150
  # The initial revision level assigned to the security descriptor.
153
151
  REVISION = 1
154
-
152
+
155
153
  # Structs for security attribute functions.
156
154
  # Holds the retrieved user access token.
157
155
  TOKEN_USER = struct ['void * SID', 'DWORD ATTRIBUTES']
158
-
156
+
159
157
  # Contains the security descriptor, this gets passed to the
160
158
  # function that constructs the shared memory map.
161
159
  SECURITY_ATTRIBUTES = struct ['DWORD nLength',
162
160
  'LPVOID lpSecurityDescriptor',
163
161
  'BOOL bInheritHandle']
164
-
162
+
165
163
  # The security descriptor holds security information.
166
164
  SECURITY_DESCRIPTOR = struct ['UCHAR Revision', 'UCHAR Sbz1',
167
165
  'USHORT Control', 'LPVOID Owner',
168
166
  'LPVOID Group', 'LPVOID Sacl',
169
167
  'LPVOID Dacl']
170
-
168
+
171
169
  # The COPYDATASTRUCT is used to send WM_COPYDATA messages
172
170
  COPYDATASTRUCT = if RUBY_ENGINE == "jruby"
173
171
  struct ['ULONG_PTR dwData', 'DWORD cbData', 'LPVOID lpData']
174
172
  else
175
173
  struct ['uintptr_t dwData', 'DWORD cbData', 'LPVOID lpData']
176
174
  end
177
-
175
+
178
176
  # Compatibility for security attribute retrieval.
179
177
  if RUBY_VERSION < "1.9"
180
178
  # Alias functions to > 1.9 capitalization
@@ -196,15 +194,15 @@ module Net
196
194
  alias_method new_name, name
197
195
  module_function new_name
198
196
  end
199
-
197
+
200
198
  def self.malloc_ptr(size)
201
199
  return DL.malloc(size)
202
200
  end
203
-
201
+
204
202
  def self.get_ptr(data)
205
203
  return data.to_ptr
206
204
  end
207
-
205
+
208
206
  def self.set_ptr_data(ptr, data)
209
207
  ptr[0] = data
210
208
  end
@@ -220,15 +218,15 @@ module Net
220
218
  attach_function :malloc, [:size_t], :pointer
221
219
  attach_function :free, [:pointer], :void
222
220
  end
223
-
221
+
224
222
  def self.malloc_ptr(size)
225
223
  Fiddle::Pointer.new(LibC.malloc(size), size, LibC.method(:free))
226
224
  end
227
-
225
+
228
226
  def self.get_ptr(ptr)
229
227
  return data.address
230
228
  end
231
-
229
+
232
230
  def self.set_ptr_data(ptr, data)
233
231
  ptr.write_string_length(data, data.size)
234
232
  end
@@ -236,19 +234,19 @@ module Net
236
234
  def self.malloc_ptr(size)
237
235
  return DL::CPtr.malloc(size, DL::RUBY_FREE)
238
236
  end
239
-
237
+
240
238
  def self.get_ptr(data)
241
239
  return DL::CPtr.to_ptr data
242
240
  end
243
-
241
+
244
242
  def self.set_ptr_data(ptr, data)
245
- DL::CPtr.new(ptr)[0,data.size] = data
243
+ DL::CPtr.new(ptr)[0, data.size] = data
246
244
  end
247
245
  end
248
-
246
+
249
247
  def self.get_security_attributes_for_user
250
248
  user = get_current_user
251
-
249
+
252
250
  psd_information = malloc_ptr(Win::SECURITY_DESCRIPTOR.size)
253
251
  raise_error_if_zero(
254
252
  Win.InitializeSecurityDescriptor(psd_information,
@@ -261,45 +259,46 @@ module Net
261
259
  raise_error_if_zero(
262
260
  Win.IsValidSecurityDescriptor(psd_information)
263
261
  )
264
-
262
+
265
263
  sa = Win::SECURITY_ATTRIBUTES.new(to_struct_ptr(malloc_ptr(Win::SECURITY_ATTRIBUTES.size)))
266
264
  sa.nLength = Win::SECURITY_ATTRIBUTES.size
267
265
  sa.lpSecurityDescriptor = psd_information.to_i
268
266
  sa.bInheritHandle = 1
269
-
267
+
270
268
  return sa
271
269
  end
272
-
270
+
273
271
  if RUBY_ENGINE == "jruby"
274
272
  def self.ptr_to_s(ptr, size)
275
273
  ret = ptr.to_s(size)
276
274
  ret << "\x00" while ret.size < size
277
275
  ret
278
276
  end
279
-
277
+
280
278
  def self.ptr_to_handle(phandle)
281
279
  phandle.ptr
282
280
  end
283
-
281
+
284
282
  def self.ptr_to_dword(ptr)
285
283
  first = ptr.ptr.to_i
286
- second = ptr_to_s(ptr,Win::SIZEOF_DWORD).unpack('L')[0]
284
+ second = ptr_to_s(ptr, Win::SIZEOF_DWORD).unpack('L')[0]
287
285
  raise "Error" unless first == second
286
+
288
287
  first
289
288
  end
290
-
289
+
291
290
  def self.to_token_user(ptoken_information)
292
291
  TOKEN_USER.new(ptoken_information.to_ptr)
293
292
  end
294
-
293
+
295
294
  def self.to_struct_ptr(ptr)
296
295
  ptr.to_ptr
297
296
  end
298
-
297
+
299
298
  def self.get_sid(user)
300
- ptr_to_s(user.to_ptr.ptr,Win::SIZEOF_DWORD).unpack('L')[0]
299
+ ptr_to_s(user.to_ptr.ptr, Win::SIZEOF_DWORD).unpack('L')[0]
301
300
  end
302
-
301
+
303
302
  def self.get_sid_ptr(user)
304
303
  user.to_ptr.ptr
305
304
  end
@@ -307,39 +306,39 @@ module Net
307
306
  def self.get_sid(user)
308
307
  user.SID
309
308
  end
310
-
309
+
311
310
  def self.ptr_to_handle(phandle)
312
311
  phandle.ptr.to_i
313
312
  end
314
-
313
+
315
314
  def self.to_struct_ptr(ptr)
316
315
  ptr
317
316
  end
318
-
317
+
319
318
  def self.ptr_to_dword(ptr)
320
319
  ptr.to_s(Win::SIZEOF_DWORD).unpack('L')[0]
321
320
  end
322
-
321
+
323
322
  def self.to_token_user(ptoken_information)
324
323
  TOKEN_USER.new(ptoken_information)
325
324
  end
326
-
325
+
327
326
  def self.get_sid_ptr(user)
328
327
  user.SID
329
328
  end
330
329
  end
331
-
330
+
332
331
  def self.get_current_user
333
332
  token_handle = open_process_token(Win.GetCurrentProcess,
334
333
  Win::TOKEN_QUERY)
335
334
  token_user = get_token_information(token_handle,
336
- Win::TOKEN_USER_INFORMATION_CLASS)
335
+ Win::TOKEN_USER_INFORMATION_CLASS)
337
336
  return token_user
338
337
  end
339
-
338
+
340
339
  def self.open_process_token(process_handle, desired_access)
341
340
  ptoken_handle = malloc_ptr(Win::SIZEOF_DWORD)
342
-
341
+
343
342
  raise_error_if_zero(
344
343
  Win.OpenProcessToken(process_handle, desired_access,
345
344
  ptoken_handle)
@@ -347,12 +346,12 @@ module Net
347
346
  token_handle = ptr_to_handle(ptoken_handle)
348
347
  return token_handle
349
348
  end
350
-
349
+
351
350
  def self.get_token_information(token_handle,
352
351
  token_information_class)
353
352
  # Hold the size of the information to be returned
354
353
  preturn_length = malloc_ptr(Win::SIZEOF_DWORD)
355
-
354
+
356
355
  # Going to throw an INSUFFICIENT_BUFFER_ERROR, but that is ok
357
356
  # here. This is retrieving the size of the information to be
358
357
  # returned.
@@ -360,7 +359,7 @@ module Net
360
359
  token_information_class,
361
360
  Win::NULL, 0, preturn_length)
362
361
  ptoken_information = malloc_ptr(ptr_to_dword(preturn_length))
363
-
362
+
364
363
  # This call is going to write the requested information to
365
364
  # the memory location referenced by token_information.
366
365
  raise_error_if_zero(
@@ -370,74 +369,76 @@ module Net
370
369
  ptoken_information.size,
371
370
  preturn_length)
372
371
  )
373
-
372
+
374
373
  return to_token_user(ptoken_information)
375
374
  end
376
-
375
+
377
376
  def self.raise_error_if_zero(result)
378
377
  if result == 0
379
378
  raise "Windows error: #{Win.GetLastError}"
380
379
  end
381
380
  end
382
-
381
+
383
382
  # Get a null-terminated string given a string.
384
383
  def self.get_cstr(str)
385
384
  return str + "\000"
386
385
  end
387
386
  end
388
-
387
+
389
388
  # This is the pseudo-socket implementation that mimics the interface of
390
389
  # a socket, translating each request into a Windows messaging call to
391
390
  # the pageant daemon. This allows pageant support to be implemented
392
391
  # simply by replacing the socket factory used by the Agent class.
393
392
  class Socket
394
393
  private_class_method :new
395
-
394
+
396
395
  # The factory method for creating a new Socket instance.
397
396
  def self.open
398
397
  new
399
398
  end
400
-
399
+
401
400
  # Create a new instance that communicates with the running pageant
402
401
  # instance. If no such instance is running, this will cause an error.
403
402
  def initialize
404
403
  @win = Win.FindWindow("Pageant", "Pageant")
405
-
404
+
406
405
  if @win.to_i == 0
407
406
  raise Net::SSH::Exception,
408
- "pageant process not running"
407
+ "pageant process not running"
409
408
  end
410
-
409
+
411
410
  @input_buffer = Net::SSH::Buffer.new
412
411
  @output_buffer = Net::SSH::Buffer.new
413
412
  end
414
-
413
+
415
414
  # Forwards the data to #send_query, ignoring any arguments after
416
415
  # the first.
417
416
  def send(data, *args)
418
417
  @input_buffer.append(data)
419
-
418
+
420
419
  ret = data.length
421
-
420
+
422
421
  while true
423
422
  return ret if @input_buffer.length < 4
423
+
424
424
  msg_length = @input_buffer.read_long + 4
425
425
  @input_buffer.reset!
426
-
426
+
427
427
  return ret if @input_buffer.length < msg_length
428
+
428
429
  msg = @input_buffer.read!(msg_length)
429
430
  @output_buffer.append(send_query(msg))
430
431
  end
431
432
  end
432
-
433
+
433
434
  # Reads +n+ bytes from the cached result of the last query. If +n+
434
435
  # is +nil+, returns all remaining data from the last query.
435
436
  def read(n = nil)
436
437
  @output_buffer.read(n)
437
438
  end
438
-
439
+
439
440
  def close; end
440
-
441
+
441
442
  # Packages the given query string and sends it to the pageant
442
443
  # process via the Windows messaging subsystem. The result is
443
444
  # cached, to be returned piece-wise when #read is called.
@@ -446,29 +447,29 @@ module Net
446
447
  filemap = 0
447
448
  ptr = nil
448
449
  id = Win.malloc_ptr(Win::SIZEOF_DWORD)
449
-
450
+
450
451
  mapname = "PageantRequest%08x" % Win.GetCurrentThreadId()
451
452
  security_attributes = Win.get_ptr Win.get_security_attributes_for_user
452
-
453
+
453
454
  filemap = Win.CreateFileMapping(Win::INVALID_HANDLE_VALUE,
454
455
  security_attributes,
455
456
  Win::PAGE_READWRITE, 0,
456
457
  AGENT_MAX_MSGLEN, mapname)
457
-
458
+
458
459
  if filemap == 0 || filemap == Win::INVALID_HANDLE_VALUE
459
460
  raise Net::SSH::Exception,
460
- "Creation of file mapping failed with error: #{Win.GetLastError}"
461
+ "Creation of file mapping failed with error: #{Win.GetLastError}"
461
462
  end
462
-
463
+
463
464
  ptr = Win.MapViewOfFile(filemap, Win::FILE_MAP_WRITE, 0, 0,
464
465
  0)
465
-
466
+
466
467
  if ptr.nil? || ptr.null?
467
468
  raise Net::SSH::Exception, "Mapping of file failed"
468
469
  end
469
-
470
+
470
471
  Win.set_ptr_data(ptr, query)
471
-
472
+
472
473
  # using struct to achieve proper alignment and field size on 64-bit platform
473
474
  cds = Win::COPYDATASTRUCT.new(Win.malloc_ptr(Win::COPYDATASTRUCT.size))
474
475
  cds.dwData = AGENT_COPYDATA_ID
@@ -476,14 +477,14 @@ module Net
476
477
  cds.lpData = Win.get_cstr(mapname)
477
478
  succ = Win.SendMessageTimeout(@win, Win::WM_COPYDATA, Win::NULL,
478
479
  cds.to_ptr, Win::SMTO_NORMAL, 5000, id)
479
-
480
+
480
481
  if succ > 0
481
482
  retlen = 4 + ptr.to_s(4).unpack("N")[0]
482
483
  res = ptr.to_s(retlen)
483
484
  else
484
485
  raise Net::SSH::Exception, "Message failed with error: #{Win.GetLastError}"
485
486
  end
486
-
487
+
487
488
  return res
488
489
  ensure
489
490
  Win.UnmapViewOfFile(ptr) unless ptr.nil? || ptr.null?
@@ -491,7 +492,6 @@ module Net
491
492
  end
492
493
  end
493
494
  end
494
-
495
495
  end
496
496
  end
497
497
  end
@@ -22,17 +22,17 @@ module Net
22
22
  # returned by OpenSSH's <tt>`ssh-add -l -E SHA256`</tt>, i.e.,
23
23
  # trailing base64 padding '=' characters are stripped and the
24
24
  # literal string +SHA256:+ is prepended.
25
- def fingerprint(algorithm='MD5')
25
+ def fingerprint(algorithm = 'MD5')
26
26
  @fingerprint ||= {}
27
27
  @fingerprint[algorithm] ||= PubKeyFingerprint.fingerprint(to_blob, algorithm)
28
28
  end
29
29
 
30
- def self.fingerprint(blob, algorithm='MD5')
30
+ def self.fingerprint(blob, algorithm = 'MD5')
31
31
  case algorithm.to_s.upcase
32
32
  when 'MD5'
33
33
  OpenSSL::Digest.hexdigest(algorithm, blob).scan(/../).join(":")
34
34
  when 'SHA256'
35
- "SHA256:#{Base64.encode64(OpenSSL::Digest.digest(algorithm, blob)).chomp.gsub(/=+\z/, '')}"
35
+ "SHA256:#{[OpenSSL::Digest.digest(algorithm, blob)].pack('m').chomp.gsub(/=+\z/, '')}"
36
36
  else
37
37
  raise OpenSSL::Digest::DigestError, "unsupported ssh key digest #{algorithm}"
38
38
  end
@@ -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"))
@@ -64,28 +63,30 @@ module Net
64
63
  key_manager = KeyManager.new(logger, options)
65
64
  keys.each { |key| key_manager.add(key) } unless keys.empty?
66
65
  keycerts.each { |keycert| key_manager.add_keycert(keycert) } unless keycerts.empty?
66
+ keycert_data.each { |data| key_manager.add_keycert_data(data) } unless keycert_data.empty?
67
67
  key_data.each { |key2| key_manager.add_key_data(key2) } unless key_data.empty?
68
68
  default_keys.each { |key| key_manager.add(key) } unless options.key?(:keys) || options.key?(:key_data)
69
69
 
70
70
  attempted = []
71
71
 
72
72
  @auth_methods.each do |name|
73
+ next unless @allowed_auth_methods.include?(name)
74
+
75
+ attempted << name
76
+
77
+ debug { "trying #{name}" }
73
78
  begin
74
- next unless @allowed_auth_methods.include?(name)
75
- attempted << name
76
-
77
- debug { "trying #{name}" }
78
- begin
79
- auth_class = Methods.const_get(name.split(/\W+/).map { |p| p.capitalize }.join)
80
- method = auth_class.new(self, key_manager: key_manager, password_prompt: options[:password_prompt])
81
- rescue NameError
82
- debug {"Mechanism #{name} was requested, but isn't a known type. Ignoring it."}
83
- next
84
- end
85
-
86
- return true if method.authenticate(next_service, username, password)
87
- rescue Net::SSH::Authentication::DisallowedMethod
79
+ auth_class = Methods.const_get(name.split(/\W+/).map { |p| p.capitalize }.join)
80
+ method = auth_class.new(self,
81
+ key_manager: key_manager, password_prompt: options[:password_prompt],
82
+ pubkey_algorithms: options[:pubkey_algorithms] || nil)
83
+ rescue NameError
84
+ debug {"Mechanism #{name} was requested, but isn't a known type. Ignoring it."}
85
+ next
88
86
  end
87
+
88
+ return true if method.authenticate(next_service, username, password)
89
+ rescue Net::SSH::Authentication::DisallowedMethod
89
90
  end
90
91
 
91
92
  error { "all authorization methods failed (tried #{attempted.join(', ')})" }
@@ -129,6 +130,7 @@ module Net
129
130
  def expect_message(type)
130
131
  message = next_message
131
132
  raise Net::SSH::Exception, "expected #{type}, got #{message.type} (#{message})" unless message.type == type
133
+
132
134
  message
133
135
  end
134
136
 
@@ -153,6 +155,12 @@ module Net
153
155
  Array(options[:keycerts])
154
156
  end
155
157
 
158
+ # Returns an array of the keycert data that should be used when
159
+ # attempting any key-based authentication mechanism.
160
+ def keycert_data
161
+ Array(options[:keycert_data])
162
+ end
163
+
156
164
  # Returns an array of the key data that should be used when
157
165
  # attempting any key-based authentication mechanism.
158
166
  def key_data