net-ssh 5.0.0.beta1 → 5.0.0.beta2

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