net-ssh-backports 6.3.0.backports

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 (111) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/ci.yml +93 -0
  3. data/.gitignore +13 -0
  4. data/.rubocop.yml +21 -0
  5. data/.rubocop_todo.yml +1074 -0
  6. data/.travis.yml +51 -0
  7. data/CHANGES.txt +698 -0
  8. data/Gemfile +13 -0
  9. data/Gemfile.noed25519 +12 -0
  10. data/ISSUE_TEMPLATE.md +30 -0
  11. data/LICENSE.txt +19 -0
  12. data/Manifest +132 -0
  13. data/README.md +287 -0
  14. data/Rakefile +105 -0
  15. data/THANKS.txt +110 -0
  16. data/appveyor.yml +58 -0
  17. data/lib/net/ssh/authentication/agent.rb +284 -0
  18. data/lib/net/ssh/authentication/certificate.rb +183 -0
  19. data/lib/net/ssh/authentication/constants.rb +20 -0
  20. data/lib/net/ssh/authentication/ed25519.rb +185 -0
  21. data/lib/net/ssh/authentication/ed25519_loader.rb +31 -0
  22. data/lib/net/ssh/authentication/key_manager.rb +297 -0
  23. data/lib/net/ssh/authentication/methods/abstract.rb +69 -0
  24. data/lib/net/ssh/authentication/methods/hostbased.rb +72 -0
  25. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +77 -0
  26. data/lib/net/ssh/authentication/methods/none.rb +34 -0
  27. data/lib/net/ssh/authentication/methods/password.rb +80 -0
  28. data/lib/net/ssh/authentication/methods/publickey.rb +95 -0
  29. data/lib/net/ssh/authentication/pageant.rb +497 -0
  30. data/lib/net/ssh/authentication/pub_key_fingerprint.rb +43 -0
  31. data/lib/net/ssh/authentication/session.rb +163 -0
  32. data/lib/net/ssh/buffer.rb +434 -0
  33. data/lib/net/ssh/buffered_io.rb +202 -0
  34. data/lib/net/ssh/config.rb +406 -0
  35. data/lib/net/ssh/connection/channel.rb +695 -0
  36. data/lib/net/ssh/connection/constants.rb +33 -0
  37. data/lib/net/ssh/connection/event_loop.rb +123 -0
  38. data/lib/net/ssh/connection/keepalive.rb +59 -0
  39. data/lib/net/ssh/connection/session.rb +712 -0
  40. data/lib/net/ssh/connection/term.rb +180 -0
  41. data/lib/net/ssh/errors.rb +106 -0
  42. data/lib/net/ssh/key_factory.rb +218 -0
  43. data/lib/net/ssh/known_hosts.rb +264 -0
  44. data/lib/net/ssh/loggable.rb +62 -0
  45. data/lib/net/ssh/packet.rb +106 -0
  46. data/lib/net/ssh/prompt.rb +62 -0
  47. data/lib/net/ssh/proxy/command.rb +123 -0
  48. data/lib/net/ssh/proxy/errors.rb +16 -0
  49. data/lib/net/ssh/proxy/http.rb +98 -0
  50. data/lib/net/ssh/proxy/https.rb +50 -0
  51. data/lib/net/ssh/proxy/jump.rb +54 -0
  52. data/lib/net/ssh/proxy/socks4.rb +67 -0
  53. data/lib/net/ssh/proxy/socks5.rb +140 -0
  54. data/lib/net/ssh/service/forward.rb +426 -0
  55. data/lib/net/ssh/test/channel.rb +147 -0
  56. data/lib/net/ssh/test/extensions.rb +173 -0
  57. data/lib/net/ssh/test/kex.rb +46 -0
  58. data/lib/net/ssh/test/local_packet.rb +53 -0
  59. data/lib/net/ssh/test/packet.rb +101 -0
  60. data/lib/net/ssh/test/remote_packet.rb +40 -0
  61. data/lib/net/ssh/test/script.rb +180 -0
  62. data/lib/net/ssh/test/socket.rb +65 -0
  63. data/lib/net/ssh/test.rb +94 -0
  64. data/lib/net/ssh/transport/algorithms.rb +502 -0
  65. data/lib/net/ssh/transport/cipher_factory.rb +103 -0
  66. data/lib/net/ssh/transport/constants.rb +40 -0
  67. data/lib/net/ssh/transport/ctr.rb +115 -0
  68. data/lib/net/ssh/transport/hmac/abstract.rb +97 -0
  69. data/lib/net/ssh/transport/hmac/md5.rb +10 -0
  70. data/lib/net/ssh/transport/hmac/md5_96.rb +9 -0
  71. data/lib/net/ssh/transport/hmac/none.rb +13 -0
  72. data/lib/net/ssh/transport/hmac/ripemd160.rb +11 -0
  73. data/lib/net/ssh/transport/hmac/sha1.rb +11 -0
  74. data/lib/net/ssh/transport/hmac/sha1_96.rb +9 -0
  75. data/lib/net/ssh/transport/hmac/sha2_256.rb +11 -0
  76. data/lib/net/ssh/transport/hmac/sha2_256_96.rb +9 -0
  77. data/lib/net/ssh/transport/hmac/sha2_256_etm.rb +12 -0
  78. data/lib/net/ssh/transport/hmac/sha2_512.rb +11 -0
  79. data/lib/net/ssh/transport/hmac/sha2_512_96.rb +9 -0
  80. data/lib/net/ssh/transport/hmac/sha2_512_etm.rb +12 -0
  81. data/lib/net/ssh/transport/hmac.rb +47 -0
  82. data/lib/net/ssh/transport/identity_cipher.rb +57 -0
  83. data/lib/net/ssh/transport/kex/abstract.rb +130 -0
  84. data/lib/net/ssh/transport/kex/abstract5656.rb +72 -0
  85. data/lib/net/ssh/transport/kex/curve25519_sha256.rb +39 -0
  86. data/lib/net/ssh/transport/kex/curve25519_sha256_loader.rb +30 -0
  87. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +37 -0
  88. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha256.rb +11 -0
  89. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +122 -0
  90. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +72 -0
  91. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha256.rb +11 -0
  92. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +39 -0
  93. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +21 -0
  94. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +21 -0
  95. data/lib/net/ssh/transport/kex.rb +31 -0
  96. data/lib/net/ssh/transport/key_expander.rb +30 -0
  97. data/lib/net/ssh/transport/openssl.rb +253 -0
  98. data/lib/net/ssh/transport/packet_stream.rb +280 -0
  99. data/lib/net/ssh/transport/server_version.rb +77 -0
  100. data/lib/net/ssh/transport/session.rb +354 -0
  101. data/lib/net/ssh/transport/state.rb +208 -0
  102. data/lib/net/ssh/verifiers/accept_new.rb +33 -0
  103. data/lib/net/ssh/verifiers/accept_new_or_local_tunnel.rb +33 -0
  104. data/lib/net/ssh/verifiers/always.rb +58 -0
  105. data/lib/net/ssh/verifiers/never.rb +19 -0
  106. data/lib/net/ssh/version.rb +68 -0
  107. data/lib/net/ssh.rb +330 -0
  108. data/net-ssh-public_cert.pem +20 -0
  109. data/net-ssh.gemspec +44 -0
  110. data/support/ssh_tunnel_bug.rb +65 -0
  111. metadata +271 -0
@@ -0,0 +1,497 @@
1
+ if RUBY_VERSION < "1.9"
2
+ require 'dl/import'
3
+ require 'dl/struct'
4
+ elsif RUBY_VERSION < "2.1"
5
+ require 'dl/import'
6
+ require 'dl/types'
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
20
+ end
21
+
22
+ require 'net/ssh/errors'
23
+
24
+ module Net
25
+ module SSH
26
+ module Authentication
27
+ # This module encapsulates the implementation of a socket factory that
28
+ # uses the PuTTY "pageant" utility to obtain information about SSH
29
+ # identities.
30
+ #
31
+ # This code is a slightly modified version of the original implementation
32
+ # by Guillaume Marçais (guillaume.marcais@free.fr). It is used and
33
+ # relicensed by permission.
34
+ module Pageant
35
+ # From Putty pageant.c
36
+ AGENT_MAX_MSGLEN = 8192
37
+ AGENT_COPYDATA_ID = 0x804e50ba
38
+
39
+ # The definition of the Windows methods and data structures used in
40
+ # communicating with the pageant process.
41
+ module Win # rubocop:disable Metrics/ModuleLength
42
+ # Compatibility on initialization
43
+ if RUBY_VERSION < "1.9"
44
+ extend DL::Importable
45
+
46
+ dlload 'user32.dll'
47
+ dlload 'kernel32.dll'
48
+ dlload 'advapi32.dll'
49
+
50
+ SIZEOF_DWORD = DL.sizeof('L')
51
+ elsif RUBY_VERSION < "2.1"
52
+ extend DL::Importer
53
+ dlload 'user32.dll','kernel32.dll', 'advapi32.dll'
54
+ include DL::Win32Types
55
+
56
+ SIZEOF_DWORD = DL::SIZEOF_LONG
57
+ else
58
+ extend Fiddle::Importer
59
+ dlload 'user32.dll','kernel32.dll', 'advapi32.dll'
60
+ include Fiddle::Win32Types
61
+ SIZEOF_DWORD = Fiddle::SIZEOF_LONG
62
+ end
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
69
+ typealias("LPCTSTR", "char *") # From winnt.h
70
+ typealias("LPVOID", "void *") # From winnt.h
71
+ typealias("LPCVOID", "const void *") # From windef.h
72
+ typealias("LRESULT", "long") # From windef.h
73
+ typealias("WPARAM", "unsigned int *") # From windef.h
74
+ typealias("LPARAM", "long *") # From windef.h
75
+ typealias("PDWORD_PTR", "long *") # From basetsd.h
76
+ typealias("USHORT", "unsigned short") # From windef.h
77
+
78
+ # From winbase.h, winnt.h
79
+ INVALID_HANDLE_VALUE = -1
80
+ NULL = nil
81
+ PAGE_READWRITE = 0x0004
82
+ FILE_MAP_WRITE = 2
83
+ WM_COPYDATA = 74
84
+
85
+ SMTO_NORMAL = 0 # From winuser.h
86
+
87
+ SUFFIX = if RUBY_ENGINE == "jruby"
88
+ "A"
89
+ else
90
+ ""
91
+ end
92
+
93
+ # args: lpClassName, lpWindowName
94
+ extern "HWND FindWindow#{SUFFIX}(LPCTSTR, LPCTSTR)"
95
+
96
+ # args: none
97
+ extern 'DWORD GetCurrentThreadId()'
98
+
99
+ # args: hFile, (ignored), flProtect, dwMaximumSizeHigh,
100
+ # dwMaximumSizeLow, lpName
101
+ extern "HANDLE CreateFileMapping#{SUFFIX}(HANDLE, void *, DWORD, " +
102
+ "DWORD, DWORD, LPCTSTR)"
103
+
104
+ # args: hFileMappingObject, dwDesiredAccess, dwFileOffsetHigh,
105
+ # dwfileOffsetLow, dwNumberOfBytesToMap
106
+ extern 'LPVOID MapViewOfFile(HANDLE, DWORD, DWORD, DWORD, DWORD)'
107
+
108
+ # args: lpBaseAddress
109
+ extern 'BOOL UnmapViewOfFile(LPCVOID)'
110
+
111
+ # args: hObject
112
+ extern 'BOOL CloseHandle(HANDLE)'
113
+
114
+ # args: hWnd, Msg, wParam, lParam, fuFlags, uTimeout, lpdwResult
115
+ extern "LRESULT SendMessageTimeout#{SUFFIX}(HWND, UINT, WPARAM, LPARAM, " +
116
+ "UINT, UINT, PDWORD_PTR)"
117
+
118
+ # args: none
119
+ extern 'DWORD GetLastError()'
120
+
121
+ # args: none
122
+ extern 'HANDLE GetCurrentProcess()'
123
+
124
+ # args: hProcessHandle, dwDesiredAccess, (out) phNewTokenHandle
125
+ extern 'BOOL OpenProcessToken(HANDLE, DWORD, PHANDLE)'
126
+
127
+ # args: hTokenHandle, uTokenInformationClass,
128
+ # (out) lpTokenInformation, dwTokenInformationLength
129
+ # (out) pdwInfoReturnLength
130
+ extern 'BOOL GetTokenInformation(HANDLE, UINT, LPVOID, DWORD, ' +
131
+ 'PDWORD)'
132
+
133
+ # args: (out) lpSecurityDescriptor, dwRevisionLevel
134
+ extern 'BOOL InitializeSecurityDescriptor(LPVOID, DWORD)'
135
+
136
+ # args: (out) lpSecurityDescriptor, lpOwnerSid, bOwnerDefaulted
137
+ extern 'BOOL SetSecurityDescriptorOwner(LPVOID, LPVOID, BOOL)'
138
+
139
+ # args: pSecurityDescriptor
140
+ extern 'BOOL IsValidSecurityDescriptor(LPVOID)'
141
+
142
+ # Constants needed for security attribute retrieval.
143
+ # Specifies the access mask corresponding to the desired access
144
+ # rights.
145
+ TOKEN_QUERY = 0x8
146
+
147
+ # The value of TOKEN_USER from the TOKEN_INFORMATION_CLASS enum.
148
+ TOKEN_USER_INFORMATION_CLASS = 1
149
+
150
+ # The initial revision level assigned to the security descriptor.
151
+ REVISION = 1
152
+
153
+ # Structs for security attribute functions.
154
+ # Holds the retrieved user access token.
155
+ TOKEN_USER = struct ['void * SID', 'DWORD ATTRIBUTES']
156
+
157
+ # Contains the security descriptor, this gets passed to the
158
+ # function that constructs the shared memory map.
159
+ SECURITY_ATTRIBUTES = struct ['DWORD nLength',
160
+ 'LPVOID lpSecurityDescriptor',
161
+ 'BOOL bInheritHandle']
162
+
163
+ # The security descriptor holds security information.
164
+ SECURITY_DESCRIPTOR = struct ['UCHAR Revision', 'UCHAR Sbz1',
165
+ 'USHORT Control', 'LPVOID Owner',
166
+ 'LPVOID Group', 'LPVOID Sacl',
167
+ 'LPVOID Dacl']
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
+
176
+ # Compatibility for security attribute retrieval.
177
+ if RUBY_VERSION < "1.9"
178
+ # Alias functions to > 1.9 capitalization
179
+ %w[findWindow
180
+ getCurrentProcess
181
+ initializeSecurityDescriptor
182
+ setSecurityDescriptorOwner
183
+ isValidSecurityDescriptor
184
+ openProcessToken
185
+ getTokenInformation
186
+ getLastError
187
+ getCurrentThreadId
188
+ createFileMapping
189
+ mapViewOfFile
190
+ sendMessageTimeout
191
+ unmapViewOfFile
192
+ closeHandle].each do |name|
193
+ new_name = name[0].chr.upcase + name[1..name.length]
194
+ alias_method new_name, name
195
+ module_function new_name
196
+ end
197
+
198
+ def self.malloc_ptr(size)
199
+ return DL.malloc(size)
200
+ end
201
+
202
+ def self.get_ptr(data)
203
+ return data.to_ptr
204
+ end
205
+
206
+ def self.set_ptr_data(ptr, data)
207
+ ptr[0] = data
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
233
+ else
234
+ def self.malloc_ptr(size)
235
+ return DL::CPtr.malloc(size, DL::RUBY_FREE)
236
+ end
237
+
238
+ def self.get_ptr(data)
239
+ return DL::CPtr.to_ptr data
240
+ end
241
+
242
+ def self.set_ptr_data(ptr, data)
243
+ DL::CPtr.new(ptr)[0,data.size] = data
244
+ end
245
+ end
246
+
247
+ def self.get_security_attributes_for_user
248
+ user = get_current_user
249
+
250
+ psd_information = malloc_ptr(Win::SECURITY_DESCRIPTOR.size)
251
+ raise_error_if_zero(
252
+ Win.InitializeSecurityDescriptor(psd_information,
253
+ Win::REVISION)
254
+ )
255
+ raise_error_if_zero(
256
+ Win.SetSecurityDescriptorOwner(psd_information, get_sid_ptr(user),
257
+ 0)
258
+ )
259
+ raise_error_if_zero(
260
+ Win.IsValidSecurityDescriptor(psd_information)
261
+ )
262
+
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
267
+
268
+ return sa
269
+ end
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
+
287
+ first
288
+ end
289
+
290
+ def self.to_token_user(ptoken_information)
291
+ TOKEN_USER.new(ptoken_information.to_ptr)
292
+ end
293
+
294
+ def self.to_struct_ptr(ptr)
295
+ ptr.to_ptr
296
+ end
297
+
298
+ def self.get_sid(user)
299
+ ptr_to_s(user.to_ptr.ptr,Win::SIZEOF_DWORD).unpack('L')[0]
300
+ end
301
+
302
+ def self.get_sid_ptr(user)
303
+ user.to_ptr.ptr
304
+ end
305
+ else
306
+ def self.get_sid(user)
307
+ user.SID
308
+ end
309
+
310
+ def self.ptr_to_handle(phandle)
311
+ phandle.ptr.to_i
312
+ end
313
+
314
+ def self.to_struct_ptr(ptr)
315
+ ptr
316
+ end
317
+
318
+ def self.ptr_to_dword(ptr)
319
+ ptr.to_s(Win::SIZEOF_DWORD).unpack('L')[0]
320
+ end
321
+
322
+ def self.to_token_user(ptoken_information)
323
+ TOKEN_USER.new(ptoken_information)
324
+ end
325
+
326
+ def self.get_sid_ptr(user)
327
+ user.SID
328
+ end
329
+ end
330
+
331
+ def self.get_current_user
332
+ token_handle = open_process_token(Win.GetCurrentProcess,
333
+ Win::TOKEN_QUERY)
334
+ token_user = get_token_information(token_handle,
335
+ Win::TOKEN_USER_INFORMATION_CLASS)
336
+ return token_user
337
+ end
338
+
339
+ def self.open_process_token(process_handle, desired_access)
340
+ ptoken_handle = malloc_ptr(Win::SIZEOF_DWORD)
341
+
342
+ raise_error_if_zero(
343
+ Win.OpenProcessToken(process_handle, desired_access,
344
+ ptoken_handle)
345
+ )
346
+ token_handle = ptr_to_handle(ptoken_handle)
347
+ return token_handle
348
+ end
349
+
350
+ def self.get_token_information(token_handle,
351
+ token_information_class)
352
+ # Hold the size of the information to be returned
353
+ preturn_length = malloc_ptr(Win::SIZEOF_DWORD)
354
+
355
+ # Going to throw an INSUFFICIENT_BUFFER_ERROR, but that is ok
356
+ # here. This is retrieving the size of the information to be
357
+ # returned.
358
+ Win.GetTokenInformation(token_handle,
359
+ token_information_class,
360
+ Win::NULL, 0, preturn_length)
361
+ ptoken_information = malloc_ptr(ptr_to_dword(preturn_length))
362
+
363
+ # This call is going to write the requested information to
364
+ # the memory location referenced by token_information.
365
+ raise_error_if_zero(
366
+ Win.GetTokenInformation(token_handle,
367
+ token_information_class,
368
+ ptoken_information,
369
+ ptoken_information.size,
370
+ preturn_length)
371
+ )
372
+
373
+ return to_token_user(ptoken_information)
374
+ end
375
+
376
+ def self.raise_error_if_zero(result)
377
+ if result == 0
378
+ raise "Windows error: #{Win.GetLastError}"
379
+ end
380
+ end
381
+
382
+ # Get a null-terminated string given a string.
383
+ def self.get_cstr(str)
384
+ return str + "\000"
385
+ end
386
+ end
387
+
388
+ # This is the pseudo-socket implementation that mimics the interface of
389
+ # a socket, translating each request into a Windows messaging call to
390
+ # the pageant daemon. This allows pageant support to be implemented
391
+ # simply by replacing the socket factory used by the Agent class.
392
+ class Socket
393
+ private_class_method :new
394
+
395
+ # The factory method for creating a new Socket instance.
396
+ def self.open
397
+ new
398
+ end
399
+
400
+ # Create a new instance that communicates with the running pageant
401
+ # instance. If no such instance is running, this will cause an error.
402
+ def initialize
403
+ @win = Win.FindWindow("Pageant", "Pageant")
404
+
405
+ if @win.to_i == 0
406
+ raise Net::SSH::Exception,
407
+ "pageant process not running"
408
+ end
409
+
410
+ @input_buffer = Net::SSH::Buffer.new
411
+ @output_buffer = Net::SSH::Buffer.new
412
+ end
413
+
414
+ # Forwards the data to #send_query, ignoring any arguments after
415
+ # the first.
416
+ def send(data, *args)
417
+ @input_buffer.append(data)
418
+
419
+ ret = data.length
420
+
421
+ while true
422
+ return ret if @input_buffer.length < 4
423
+
424
+ msg_length = @input_buffer.read_long + 4
425
+ @input_buffer.reset!
426
+
427
+ return ret if @input_buffer.length < msg_length
428
+
429
+ msg = @input_buffer.read!(msg_length)
430
+ @output_buffer.append(send_query(msg))
431
+ end
432
+ end
433
+
434
+ # Reads +n+ bytes from the cached result of the last query. If +n+
435
+ # is +nil+, returns all remaining data from the last query.
436
+ def read(n = nil)
437
+ @output_buffer.read(n)
438
+ end
439
+
440
+ def close; end
441
+
442
+ # Packages the given query string and sends it to the pageant
443
+ # process via the Windows messaging subsystem. The result is
444
+ # cached, to be returned piece-wise when #read is called.
445
+ def send_query(query)
446
+ res = nil
447
+ filemap = 0
448
+ ptr = nil
449
+ id = Win.malloc_ptr(Win::SIZEOF_DWORD)
450
+
451
+ mapname = "PageantRequest%08x" % Win.GetCurrentThreadId()
452
+ security_attributes = Win.get_ptr Win.get_security_attributes_for_user
453
+
454
+ filemap = Win.CreateFileMapping(Win::INVALID_HANDLE_VALUE,
455
+ security_attributes,
456
+ Win::PAGE_READWRITE, 0,
457
+ AGENT_MAX_MSGLEN, mapname)
458
+
459
+ if filemap == 0 || filemap == Win::INVALID_HANDLE_VALUE
460
+ raise Net::SSH::Exception,
461
+ "Creation of file mapping failed with error: #{Win.GetLastError}"
462
+ end
463
+
464
+ ptr = Win.MapViewOfFile(filemap, Win::FILE_MAP_WRITE, 0, 0,
465
+ 0)
466
+
467
+ if ptr.nil? || ptr.null?
468
+ raise Net::SSH::Exception, "Mapping of file failed"
469
+ end
470
+
471
+ Win.set_ptr_data(ptr, query)
472
+
473
+ # using struct to achieve proper alignment and field size on 64-bit platform
474
+ cds = Win::COPYDATASTRUCT.new(Win.malloc_ptr(Win::COPYDATASTRUCT.size))
475
+ cds.dwData = AGENT_COPYDATA_ID
476
+ cds.cbData = mapname.size + 1
477
+ cds.lpData = Win.get_cstr(mapname)
478
+ succ = Win.SendMessageTimeout(@win, Win::WM_COPYDATA, Win::NULL,
479
+ cds.to_ptr, Win::SMTO_NORMAL, 5000, id)
480
+
481
+ if succ > 0
482
+ retlen = 4 + ptr.to_s(4).unpack("N")[0]
483
+ res = ptr.to_s(retlen)
484
+ else
485
+ raise Net::SSH::Exception, "Message failed with error: #{Win.GetLastError}"
486
+ end
487
+
488
+ return res
489
+ ensure
490
+ Win.UnmapViewOfFile(ptr) unless ptr.nil? || ptr.null?
491
+ Win.CloseHandle(filemap) if filemap != 0
492
+ end
493
+ end
494
+ end
495
+ end
496
+ end
497
+ end
@@ -0,0 +1,43 @@
1
+ require 'openssl'
2
+
3
+ module Net
4
+ module SSH
5
+ module Authentication
6
+ # Public key fingerprinting utility module - internal not part of API.
7
+ # This is included in pubkey classes and called from there. All RSA, DSA, and ECC keys
8
+ # are supported.
9
+ #
10
+ # require 'net/ssh'
11
+ # my_pubkey_text = File.read('/path/to/id_ed25519.pub')
12
+ # #=> "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDB2NBh4GJPPUN1kXPMu8b633Xcv55WoKC3OkBjFAbzJ alice@example.com"
13
+ # my_pubkey = Net::SSH::KeyFactory.load_data_public_key(my_pubkey_text)
14
+ # #=> #<Net::SSH::Authentication::ED25519::PubKey:0x00007fc8e91819b0
15
+ # my_pubkey.fingerprint
16
+ # #=> "2f:7f:97:21:76:a4:0f:38:c4:fe:d8:b4:6a:39:72:30"
17
+ # my_pubkey.fingerprint('SHA256')
18
+ # #=> "SHA256:u6mXnY8P1b0FODGp8mckqOB33u8+jvkSCtJbD5Q9klg"
19
+ module PubKeyFingerprint # :nodoc:
20
+ # Return the key's fingerprint. Algorithm may be either +MD5+ (default),
21
+ # or +SHA256+. For +SHA256+, fingerprints are in the same format
22
+ # returned by OpenSSH's <tt>`ssh-add -l -E SHA256`</tt>, i.e.,
23
+ # trailing base64 padding '=' characters are stripped and the
24
+ # literal string +SHA256:+ is prepended.
25
+ def fingerprint(algorithm='MD5')
26
+ @fingerprint ||= {}
27
+ @fingerprint[algorithm] ||= PubKeyFingerprint.fingerprint(to_blob, algorithm)
28
+ end
29
+
30
+ def self.fingerprint(blob, algorithm='MD5')
31
+ case algorithm.to_s.upcase
32
+ when 'MD5'
33
+ OpenSSL::Digest.hexdigest(algorithm, blob).scan(/../).join(":")
34
+ when 'SHA256'
35
+ "SHA256:#{Base64.encode64(OpenSSL::Digest.digest(algorithm, blob)).chomp.gsub(/=+\z/, '')}"
36
+ else
37
+ raise OpenSSL::Digest::DigestError, "unsupported ssh key digest #{algorithm}"
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,163 @@
1
+ require 'net/ssh/loggable'
2
+ require 'net/ssh/transport/constants'
3
+ require 'net/ssh/authentication/constants'
4
+ require 'net/ssh/authentication/key_manager'
5
+ require 'net/ssh/authentication/methods/none'
6
+ require 'net/ssh/authentication/methods/publickey'
7
+ require 'net/ssh/authentication/methods/hostbased'
8
+ require 'net/ssh/authentication/methods/password'
9
+ require 'net/ssh/authentication/methods/keyboard_interactive'
10
+
11
+ module Net
12
+ module SSH
13
+ module Authentication
14
+ # Raised if the current authentication method is not allowed
15
+ class DisallowedMethod < Net::SSH::Exception
16
+ end
17
+
18
+ # Represents an authentication session. It manages the authentication of
19
+ # a user over an established connection (the "transport" object, see
20
+ # Net::SSH::Transport::Session).
21
+ #
22
+ # The use of an authentication session to manage user authentication is
23
+ # internal to Net::SSH (specifically Net::SSH.start). Consumers of the
24
+ # Net::SSH library will never need to access this class directly.
25
+ class Session
26
+ include Loggable
27
+ include Constants
28
+ include Transport::Constants
29
+
30
+ # transport layer abstraction
31
+ attr_reader :transport
32
+
33
+ # the list of authentication methods to try
34
+ attr_reader :auth_methods
35
+
36
+ # the list of authentication methods that are allowed
37
+ attr_reader :allowed_auth_methods
38
+
39
+ # a hash of options, given at construction time
40
+ attr_reader :options
41
+
42
+ # Instantiates a new Authentication::Session object over the given
43
+ # transport layer abstraction.
44
+ def initialize(transport, options={})
45
+ self.logger = transport.logger
46
+ @transport = transport
47
+
48
+ @auth_methods = options[:auth_methods] || Net::SSH::Config.default_auth_methods
49
+ @options = options
50
+
51
+ @allowed_auth_methods = @auth_methods
52
+ end
53
+
54
+ # Attempts to authenticate the given user, in preparation for the next
55
+ # service request. Returns true if an authentication method succeeds in
56
+ # authenticating the user, and false otherwise.
57
+ def authenticate(next_service, username, password=nil)
58
+ debug { "beginning authentication of `#{username}'" }
59
+
60
+ transport.send_message(transport.service_request("ssh-userauth"))
61
+ expect_message(SERVICE_ACCEPT)
62
+
63
+ key_manager = KeyManager.new(logger, options)
64
+ keys.each { |key| key_manager.add(key) } unless keys.empty?
65
+ keycerts.each { |keycert| key_manager.add_keycert(keycert) } unless keycerts.empty?
66
+ key_data.each { |key2| key_manager.add_key_data(key2) } unless key_data.empty?
67
+ default_keys.each { |key| key_manager.add(key) } unless options.key?(:keys) || options.key?(:key_data)
68
+
69
+ attempted = []
70
+
71
+ @auth_methods.each do |name|
72
+ next unless @allowed_auth_methods.include?(name)
73
+
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
87
+ end
88
+
89
+ error { "all authorization methods failed (tried #{attempted.join(', ')})" }
90
+ return false
91
+ ensure
92
+ key_manager.finish if key_manager
93
+ end
94
+
95
+ # Blocks until a packet is received. It silently handles USERAUTH_BANNER
96
+ # packets, and will raise an error if any packet is received that is not
97
+ # valid during user authentication.
98
+ def next_message
99
+ loop do
100
+ packet = transport.next_message
101
+
102
+ case packet.type
103
+ when USERAUTH_BANNER
104
+ info { packet[:message] }
105
+ # TODO add a hook for people to retrieve the banner when it is sent
106
+
107
+ when USERAUTH_FAILURE
108
+ @allowed_auth_methods = packet[:authentications].split(/,/)
109
+ debug { "allowed methods: #{packet[:authentications]}" }
110
+ return packet
111
+
112
+ when USERAUTH_METHOD_RANGE, SERVICE_ACCEPT
113
+ return packet
114
+
115
+ when USERAUTH_SUCCESS
116
+ transport.hint :authenticated
117
+ return packet
118
+
119
+ else
120
+ raise Net::SSH::Exception, "unexpected message #{packet.type} (#{packet})"
121
+ end
122
+ end
123
+ end
124
+
125
+ # Blocks until a packet is received, and returns it if it is of the given
126
+ # type. If it is not, an exception is raised.
127
+ def expect_message(type)
128
+ message = next_message
129
+ raise Net::SSH::Exception, "expected #{type}, got #{message.type} (#{message})" unless message.type == type
130
+
131
+ message
132
+ end
133
+
134
+ private
135
+
136
+ # Returns an array of paths to the key files usually defined
137
+ # by system default.
138
+ def default_keys
139
+ %w[~/.ssh/id_ed25519 ~/.ssh/id_rsa ~/.ssh/id_dsa ~/.ssh/id_ecdsa
140
+ ~/.ssh2/id_ed25519 ~/.ssh2/id_rsa ~/.ssh2/id_dsa ~/.ssh2/id_ecdsa]
141
+ end
142
+
143
+ # Returns an array of paths to the key files that should be used when
144
+ # attempting any key-based authentication mechanism.
145
+ def keys
146
+ Array(options[:keys])
147
+ end
148
+
149
+ # Returns an array of paths to the keycert files that should be used when
150
+ # attempting any key-based authentication mechanism.
151
+ def keycerts
152
+ Array(options[:keycerts])
153
+ end
154
+
155
+ # Returns an array of the key data that should be used when
156
+ # attempting any key-based authentication mechanism.
157
+ def key_data
158
+ Array(options[:key_data])
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end