net-ssh 3.2.0 → 7.2.0.rc1

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 (210) hide show
  1. checksums.yaml +5 -5
  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 +93 -0
  8. data/.github/workflows/rubocop.yml +16 -0
  9. data/.gitignore +13 -0
  10. data/.rubocop.yml +22 -0
  11. data/.rubocop_todo.yml +1081 -0
  12. data/CHANGES.txt +237 -7
  13. data/DEVELOPMENT.md +23 -0
  14. data/Dockerfile +27 -0
  15. data/Dockerfile.openssl3 +17 -0
  16. data/Gemfile +13 -0
  17. data/Gemfile.noed25519 +12 -0
  18. data/Gemfile.norbnacl +12 -0
  19. data/ISSUE_TEMPLATE.md +30 -0
  20. data/Manifest +4 -5
  21. data/README.md +298 -0
  22. data/Rakefile +125 -74
  23. data/SECURITY.md +4 -0
  24. data/appveyor.yml +58 -0
  25. data/docker-compose.yml +23 -0
  26. data/lib/net/ssh/authentication/agent.rb +279 -18
  27. data/lib/net/ssh/authentication/certificate.rb +183 -0
  28. data/lib/net/ssh/authentication/constants.rb +17 -15
  29. data/lib/net/ssh/authentication/ed25519.rb +186 -0
  30. data/lib/net/ssh/authentication/ed25519_loader.rb +31 -0
  31. data/lib/net/ssh/authentication/key_manager.rb +86 -39
  32. data/lib/net/ssh/authentication/methods/abstract.rb +67 -48
  33. data/lib/net/ssh/authentication/methods/hostbased.rb +34 -37
  34. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +13 -13
  35. data/lib/net/ssh/authentication/methods/none.rb +16 -19
  36. data/lib/net/ssh/authentication/methods/password.rb +27 -17
  37. data/lib/net/ssh/authentication/methods/publickey.rb +96 -55
  38. data/lib/net/ssh/authentication/pageant.rb +471 -367
  39. data/lib/net/ssh/authentication/pub_key_fingerprint.rb +43 -0
  40. data/lib/net/ssh/authentication/session.rb +131 -121
  41. data/lib/net/ssh/buffer.rb +399 -300
  42. data/lib/net/ssh/buffered_io.rb +154 -150
  43. data/lib/net/ssh/config.rb +308 -185
  44. data/lib/net/ssh/connection/channel.rb +635 -613
  45. data/lib/net/ssh/connection/constants.rb +29 -29
  46. data/lib/net/ssh/connection/event_loop.rb +123 -0
  47. data/lib/net/ssh/connection/keepalive.rb +55 -51
  48. data/lib/net/ssh/connection/session.rb +620 -551
  49. data/lib/net/ssh/connection/term.rb +125 -123
  50. data/lib/net/ssh/errors.rb +101 -99
  51. data/lib/net/ssh/key_factory.rb +197 -105
  52. data/lib/net/ssh/known_hosts.rb +214 -127
  53. data/lib/net/ssh/loggable.rb +50 -49
  54. data/lib/net/ssh/packet.rb +83 -79
  55. data/lib/net/ssh/prompt.rb +50 -81
  56. data/lib/net/ssh/proxy/command.rb +105 -90
  57. data/lib/net/ssh/proxy/errors.rb +12 -10
  58. data/lib/net/ssh/proxy/http.rb +82 -79
  59. data/lib/net/ssh/proxy/https.rb +50 -0
  60. data/lib/net/ssh/proxy/jump.rb +54 -0
  61. data/lib/net/ssh/proxy/socks4.rb +2 -6
  62. data/lib/net/ssh/proxy/socks5.rb +14 -17
  63. data/lib/net/ssh/service/forward.rb +370 -317
  64. data/lib/net/ssh/test/channel.rb +145 -136
  65. data/lib/net/ssh/test/extensions.rb +131 -110
  66. data/lib/net/ssh/test/kex.rb +34 -32
  67. data/lib/net/ssh/test/local_packet.rb +46 -44
  68. data/lib/net/ssh/test/packet.rb +89 -70
  69. data/lib/net/ssh/test/remote_packet.rb +32 -30
  70. data/lib/net/ssh/test/script.rb +156 -142
  71. data/lib/net/ssh/test/socket.rb +49 -48
  72. data/lib/net/ssh/test.rb +82 -77
  73. data/lib/net/ssh/transport/algorithms.rb +462 -359
  74. data/lib/net/ssh/transport/chacha20_poly1305_cipher.rb +117 -0
  75. data/lib/net/ssh/transport/chacha20_poly1305_cipher_loader.rb +17 -0
  76. data/lib/net/ssh/transport/cipher_factory.rb +122 -99
  77. data/lib/net/ssh/transport/constants.rb +32 -24
  78. data/lib/net/ssh/transport/ctr.rb +42 -22
  79. data/lib/net/ssh/transport/hmac/abstract.rb +81 -63
  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/sha2_256.rb +7 -11
  87. data/lib/net/ssh/transport/hmac/sha2_256_96.rb +4 -8
  88. data/lib/net/ssh/transport/hmac/sha2_256_etm.rb +12 -0
  89. data/lib/net/ssh/transport/hmac/sha2_512.rb +6 -9
  90. data/lib/net/ssh/transport/hmac/sha2_512_96.rb +4 -8
  91. data/lib/net/ssh/transport/hmac/sha2_512_etm.rb +12 -0
  92. data/lib/net/ssh/transport/hmac.rb +14 -12
  93. data/lib/net/ssh/transport/identity_cipher.rb +54 -44
  94. data/lib/net/ssh/transport/kex/abstract.rb +130 -0
  95. data/lib/net/ssh/transport/kex/abstract5656.rb +72 -0
  96. data/lib/net/ssh/transport/kex/curve25519_sha256.rb +39 -0
  97. data/lib/net/ssh/transport/kex/curve25519_sha256_loader.rb +30 -0
  98. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +33 -40
  99. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha256.rb +11 -0
  100. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +119 -213
  101. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +53 -61
  102. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha256.rb +5 -9
  103. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +36 -90
  104. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +18 -10
  105. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +18 -10
  106. data/lib/net/ssh/transport/kex.rb +15 -12
  107. data/lib/net/ssh/transport/key_expander.rb +24 -20
  108. data/lib/net/ssh/transport/openssl.rb +161 -124
  109. data/lib/net/ssh/transport/openssl_cipher_extensions.rb +8 -0
  110. data/lib/net/ssh/transport/packet_stream.rb +246 -185
  111. data/lib/net/ssh/transport/server_version.rb +55 -56
  112. data/lib/net/ssh/transport/session.rb +306 -255
  113. data/lib/net/ssh/transport/state.rb +178 -176
  114. data/lib/net/ssh/verifiers/accept_new.rb +33 -0
  115. data/lib/net/ssh/verifiers/accept_new_or_local_tunnel.rb +33 -0
  116. data/lib/net/ssh/verifiers/always.rb +58 -0
  117. data/lib/net/ssh/verifiers/never.rb +19 -0
  118. data/lib/net/ssh/version.rb +55 -53
  119. data/lib/net/ssh.rb +111 -47
  120. data/net-ssh-public_cert.pem +18 -18
  121. data/net-ssh.gemspec +38 -205
  122. data/support/ssh_tunnel_bug.rb +5 -5
  123. data.tar.gz.sig +0 -0
  124. metadata +173 -118
  125. metadata.gz.sig +0 -0
  126. data/.travis.yml +0 -18
  127. data/README.rdoc +0 -182
  128. data/lib/net/ssh/authentication/agent/java_pageant.rb +0 -85
  129. data/lib/net/ssh/authentication/agent/socket.rb +0 -178
  130. data/lib/net/ssh/ruby_compat.rb +0 -46
  131. data/lib/net/ssh/verifiers/lenient.rb +0 -30
  132. data/lib/net/ssh/verifiers/null.rb +0 -12
  133. data/lib/net/ssh/verifiers/secure.rb +0 -52
  134. data/lib/net/ssh/verifiers/strict.rb +0 -24
  135. data/setup.rb +0 -1585
  136. data/support/arcfour_check.rb +0 -20
  137. data/test/README.txt +0 -18
  138. data/test/authentication/methods/common.rb +0 -28
  139. data/test/authentication/methods/test_abstract.rb +0 -51
  140. data/test/authentication/methods/test_hostbased.rb +0 -114
  141. data/test/authentication/methods/test_keyboard_interactive.rb +0 -121
  142. data/test/authentication/methods/test_none.rb +0 -41
  143. data/test/authentication/methods/test_password.rb +0 -95
  144. data/test/authentication/methods/test_publickey.rb +0 -148
  145. data/test/authentication/test_agent.rb +0 -232
  146. data/test/authentication/test_key_manager.rb +0 -240
  147. data/test/authentication/test_session.rb +0 -107
  148. data/test/common.rb +0 -125
  149. data/test/configs/auth_off +0 -5
  150. data/test/configs/auth_on +0 -4
  151. data/test/configs/empty +0 -0
  152. data/test/configs/eqsign +0 -3
  153. data/test/configs/exact_match +0 -8
  154. data/test/configs/host_plus +0 -10
  155. data/test/configs/multihost +0 -4
  156. data/test/configs/negative_match +0 -6
  157. data/test/configs/nohost +0 -19
  158. data/test/configs/numeric_host +0 -4
  159. data/test/configs/proxy_remote_user +0 -2
  160. data/test/configs/send_env +0 -2
  161. data/test/configs/substitutes +0 -8
  162. data/test/configs/wild_cards +0 -14
  163. data/test/connection/test_channel.rb +0 -487
  164. data/test/connection/test_session.rb +0 -564
  165. data/test/integration/README.txt +0 -17
  166. data/test/integration/Vagrantfile +0 -12
  167. data/test/integration/common.rb +0 -63
  168. data/test/integration/playbook.yml +0 -56
  169. data/test/integration/test_forward.rb +0 -637
  170. data/test/integration/test_id_rsa_keys.rb +0 -96
  171. data/test/integration/test_proxy.rb +0 -93
  172. data/test/known_hosts/github +0 -1
  173. data/test/known_hosts/github_hash +0 -1
  174. data/test/manual/test_pageant.rb +0 -37
  175. data/test/start/test_connection.rb +0 -53
  176. data/test/start/test_options.rb +0 -57
  177. data/test/start/test_transport.rb +0 -28
  178. data/test/start/test_user_nil.rb +0 -27
  179. data/test/test_all.rb +0 -12
  180. data/test/test_buffer.rb +0 -433
  181. data/test/test_buffered_io.rb +0 -63
  182. data/test/test_config.rb +0 -268
  183. data/test/test_key_factory.rb +0 -191
  184. data/test/test_known_hosts.rb +0 -66
  185. data/test/transport/hmac/test_md5.rb +0 -41
  186. data/test/transport/hmac/test_md5_96.rb +0 -27
  187. data/test/transport/hmac/test_none.rb +0 -34
  188. data/test/transport/hmac/test_ripemd160.rb +0 -36
  189. data/test/transport/hmac/test_sha1.rb +0 -36
  190. data/test/transport/hmac/test_sha1_96.rb +0 -27
  191. data/test/transport/hmac/test_sha2_256.rb +0 -37
  192. data/test/transport/hmac/test_sha2_256_96.rb +0 -27
  193. data/test/transport/hmac/test_sha2_512.rb +0 -37
  194. data/test/transport/hmac/test_sha2_512_96.rb +0 -27
  195. data/test/transport/kex/test_diffie_hellman_group14_sha1.rb +0 -13
  196. data/test/transport/kex/test_diffie_hellman_group1_sha1.rb +0 -150
  197. data/test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb +0 -96
  198. data/test/transport/kex/test_diffie_hellman_group_exchange_sha256.rb +0 -19
  199. data/test/transport/kex/test_ecdh_sha2_nistp256.rb +0 -161
  200. data/test/transport/kex/test_ecdh_sha2_nistp384.rb +0 -38
  201. data/test/transport/kex/test_ecdh_sha2_nistp521.rb +0 -38
  202. data/test/transport/test_algorithms.rb +0 -328
  203. data/test/transport/test_cipher_factory.rb +0 -443
  204. data/test/transport/test_hmac.rb +0 -34
  205. data/test/transport/test_identity_cipher.rb +0 -40
  206. data/test/transport/test_packet_stream.rb +0 -1762
  207. data/test/transport/test_server_version.rb +0 -74
  208. data/test/transport/test_session.rb +0 -331
  209. data/test/transport/test_state.rb +0 -181
  210. data/test/verifiers/test_secure.rb +0 -40
@@ -13,381 +13,485 @@ else
13
13
  # For now map DL to Fiddler versus updating all the code below
14
14
  module DL
15
15
  CPtr ||= Fiddle::Pointer
16
- RUBY_FREE ||= Fiddle::RUBY_FREE
16
+ if RUBY_PLATFORM != "java"
17
+ RUBY_FREE ||= Fiddle::RUBY_FREE
18
+ end
17
19
  end
18
20
  end
19
21
 
20
22
  require 'net/ssh/errors'
21
23
 
22
- module Net; module SSH; module Authentication
23
-
24
- # This module encapsulates the implementation of a socket factory that
25
- # uses the PuTTY "pageant" utility to obtain information about SSH
26
- # identities.
27
- #
28
- # This code is a slightly modified version of the original implementation
29
- # by Guillaume Marçais (guillaume.marcais@free.fr). It is used and
30
- # relicensed by permission.
31
- module Pageant
32
-
33
- # From Putty pageant.c
34
- AGENT_MAX_MSGLEN = 8192
35
- AGENT_COPYDATA_ID = 0x804e50ba
36
-
37
- # The definition of the Windows methods and data structures used in
38
- # communicating with the pageant process.
39
- module Win
40
- # Compatibility on initialization
41
- if RUBY_VERSION < "1.9"
42
- extend DL::Importable
43
-
44
- dlload 'user32'
45
- dlload 'kernel32'
46
- dlload 'advapi32'
47
-
48
- SIZEOF_DWORD = DL.sizeof('L')
49
- elsif RUBY_VERSION < "2.1"
50
- extend DL::Importer
51
- dlload 'user32','kernel32', 'advapi32'
52
- include DL::Win32Types
53
-
54
- SIZEOF_DWORD = DL::SIZEOF_LONG
55
- else
56
- extend Fiddle::Importer
57
- dlload 'user32','kernel32', 'advapi32'
58
- include Fiddle::Win32Types
59
- SIZEOF_DWORD = Fiddle::SIZEOF_LONG
60
- end
61
-
62
- typealias("LPCTSTR", "char *") # From winnt.h
63
- typealias("LPVOID", "void *") # From winnt.h
64
- typealias("LPCVOID", "const void *") # From windef.h
65
- typealias("LRESULT", "long") # From windef.h
66
- typealias("WPARAM", "unsigned int *") # From windef.h
67
- typealias("LPARAM", "long *") # From windef.h
68
- typealias("PDWORD_PTR", "long *") # From basetsd.h
69
- typealias("USHORT", "unsigned short") # From windef.h
70
-
71
- # From winbase.h, winnt.h
72
- INVALID_HANDLE_VALUE = -1
73
- NULL = nil
74
- PAGE_READWRITE = 0x0004
75
- FILE_MAP_WRITE = 2
76
- WM_COPYDATA = 74
77
-
78
- SMTO_NORMAL = 0 # From winuser.h
79
-
80
- # args: lpClassName, lpWindowName
81
- extern 'HWND FindWindow(LPCTSTR, LPCTSTR)'
82
-
83
- # args: none
84
- extern 'DWORD GetCurrentThreadId()'
85
-
86
- # args: hFile, (ignored), flProtect, dwMaximumSizeHigh,
87
- # dwMaximumSizeLow, lpName
88
- extern 'HANDLE CreateFileMapping(HANDLE, void *, DWORD, ' +
89
- 'DWORD, DWORD, LPCTSTR)'
90
-
91
- # args: hFileMappingObject, dwDesiredAccess, dwFileOffsetHigh,
92
- # dwfileOffsetLow, dwNumberOfBytesToMap
93
- extern 'LPVOID MapViewOfFile(HANDLE, DWORD, DWORD, DWORD, DWORD)'
94
-
95
- # args: lpBaseAddress
96
- extern 'BOOL UnmapViewOfFile(LPCVOID)'
97
-
98
- # args: hObject
99
- extern 'BOOL CloseHandle(HANDLE)'
100
-
101
- # args: hWnd, Msg, wParam, lParam, fuFlags, uTimeout, lpdwResult
102
- extern 'LRESULT SendMessageTimeout(HWND, UINT, WPARAM, LPARAM, ' +
103
- 'UINT, UINT, PDWORD_PTR)'
104
-
105
- # args: none
106
- extern 'DWORD GetLastError()'
107
-
108
- # args: none
109
- extern 'HANDLE GetCurrentProcess()'
110
-
111
- # args: hProcessHandle, dwDesiredAccess, (out) phNewTokenHandle
112
- extern 'BOOL OpenProcessToken(HANDLE, DWORD, PHANDLE)'
113
-
114
- # args: hTokenHandle, uTokenInformationClass,
115
- # (out) lpTokenInformation, dwTokenInformationLength
116
- # (out) pdwInfoReturnLength
117
- extern 'BOOL GetTokenInformation(HANDLE, UINT, LPVOID, DWORD, ' +
118
- 'PDWORD)'
119
-
120
- # args: (out) lpSecurityDescriptor, dwRevisionLevel
121
- extern 'BOOL InitializeSecurityDescriptor(LPVOID, DWORD)'
122
-
123
- # args: (out) lpSecurityDescriptor, lpOwnerSid, bOwnerDefaulted
124
- extern 'BOOL SetSecurityDescriptorOwner(LPVOID, LPVOID, BOOL)'
125
-
126
- # args: pSecurityDescriptor
127
- extern 'BOOL IsValidSecurityDescriptor(LPVOID)'
128
-
129
- # Constants needed for security attribute retrieval.
130
- # Specifies the access mask corresponding to the desired access
131
- # rights.
132
- TOKEN_QUERY = 0x8
133
-
134
- # The value of TOKEN_USER from the TOKEN_INFORMATION_CLASS enum.
135
- TOKEN_USER_INFORMATION_CLASS = 1
136
-
137
- # The initial revision level assigned to the security descriptor.
138
- REVISION = 1
139
-
140
- # Structs for security attribute functions.
141
- # Holds the retrieved user access token.
142
- TOKEN_USER = struct ['void * SID', 'DWORD ATTRIBUTES']
143
-
144
- # Contains the security descriptor, this gets passed to the
145
- # function that constructs the shared memory map.
146
- SECURITY_ATTRIBUTES = struct ['DWORD nLength',
147
- 'LPVOID lpSecurityDescriptor',
148
- 'BOOL bInheritHandle']
149
-
150
- # The security descriptor holds security information.
151
- SECURITY_DESCRIPTOR = struct ['UCHAR Revision', 'UCHAR Sbz1',
152
- 'USHORT Control', 'LPVOID Owner',
153
- 'LPVOID Group', 'LPVOID Sacl',
154
- 'LPVOID Dacl']
155
-
156
- # The COPYDATASTRUCT is used to send WM_COPYDATA messages
157
- COPYDATASTRUCT = struct ['uintptr_t dwData', 'DWORD cbData', 'LPVOID lpData']
158
-
159
- # Compatibility for security attribute retrieval.
160
- if RUBY_VERSION < "1.9"
161
- # Alias functions to > 1.9 capitalization
162
- %w(findWindow
163
- getCurrentProcess
164
- initializeSecurityDescriptor
165
- setSecurityDescriptorOwner
166
- isValidSecurityDescriptor
167
- openProcessToken
168
- getTokenInformation
169
- getLastError
170
- getCurrentThreadId
171
- createFileMapping
172
- mapViewOfFile
173
- sendMessageTimeout
174
- unmapViewOfFile
175
- closeHandle).each do |name|
176
- new_name = name[0].chr.upcase + name[1..name.length]
177
- alias_method new_name, name
178
- module_function new_name
179
- end
180
-
181
- def self.malloc_ptr(size)
182
- return DL.malloc(size)
183
- end
184
-
185
- def self.get_ptr(data)
186
- return data.to_ptr
187
- end
188
-
189
- def self.set_ptr_data(ptr, data)
190
- ptr[0] = data
191
- end
192
- else
193
- def self.malloc_ptr(size)
194
- return DL::CPtr.malloc(size, DL::RUBY_FREE)
195
- end
196
-
197
- def self.get_ptr(data)
198
- return DL::CPtr.to_ptr data
199
- end
200
-
201
- def self.set_ptr_data(ptr, data)
202
- DL::CPtr.new(ptr)[0,data.size] = data
203
- end
204
- end
205
-
206
- def self.get_security_attributes_for_user
207
- user = get_current_user
208
-
209
- psd_information = malloc_ptr(Win::SECURITY_DESCRIPTOR.size)
210
- raise_error_if_zero(
211
- Win.InitializeSecurityDescriptor(psd_information,
212
- Win::REVISION))
213
- raise_error_if_zero(
214
- Win.SetSecurityDescriptorOwner(psd_information, user.SID,
215
- 0))
216
- raise_error_if_zero(
217
- Win.IsValidSecurityDescriptor(psd_information))
218
-
219
- sa = Win::SECURITY_ATTRIBUTES.new(malloc_ptr(Win::SECURITY_ATTRIBUTES.size))
220
- sa.nLength = Win::SECURITY_ATTRIBUTES.size
221
- sa.lpSecurityDescriptor = psd_information.to_i
222
- sa.bInheritHandle = 1
223
-
224
- return sa
225
- end
226
-
227
- def self.get_current_user
228
- token_handle = open_process_token(Win.GetCurrentProcess,
229
- Win::TOKEN_QUERY)
230
- token_user = get_token_information(token_handle,
231
- Win::TOKEN_USER_INFORMATION_CLASS)
232
- return token_user
233
- end
234
-
235
- def self.open_process_token(process_handle, desired_access)
236
- ptoken_handle = malloc_ptr(Win::SIZEOF_DWORD)
237
-
238
- raise_error_if_zero(
239
- Win.OpenProcessToken(process_handle, desired_access,
240
- ptoken_handle))
241
- token_handle = ptoken_handle.ptr.to_i
242
-
243
- return token_handle
244
- end
245
-
246
- def self.get_token_information(token_handle,
247
- token_information_class)
248
- # Hold the size of the information to be returned
249
- preturn_length = malloc_ptr(Win::SIZEOF_DWORD)
250
-
251
- # Going to throw an INSUFFICIENT_BUFFER_ERROR, but that is ok
252
- # here. This is retrieving the size of the information to be
253
- # returned.
254
- Win.GetTokenInformation(token_handle,
255
- token_information_class,
256
- Win::NULL, 0, preturn_length)
257
- ptoken_information = malloc_ptr(preturn_length.ptr.to_i)
258
-
259
- # This call is going to write the requested information to
260
- # the memory location referenced by token_information.
261
- raise_error_if_zero(
262
- Win.GetTokenInformation(token_handle,
263
- token_information_class,
264
- ptoken_information,
265
- ptoken_information.size,
266
- preturn_length))
267
-
268
- return TOKEN_USER.new(ptoken_information)
269
- end
270
-
271
- def self.raise_error_if_zero(result)
272
- if result == 0
273
- raise "Windows error: #{Win.GetLastError}"
274
- end
275
- end
276
-
277
- # Get a null-terminated string given a string.
278
- def self.get_cstr(str)
279
- return str + "\000"
280
- end
281
- end
282
-
283
- # This is the pseudo-socket implementation that mimics the interface of
284
- # a socket, translating each request into a Windows messaging call to
285
- # the pageant daemon. This allows pageant support to be implemented
286
- # simply by replacing the socket factory used by the Agent class.
287
- class Socket
288
-
289
- private_class_method :new
290
-
291
- # The factory method for creating a new Socket instance. The location
292
- # parameter is ignored, and is only needed for compatibility with
293
- # the general Socket interface.
294
- def self.open(location=nil)
295
- new
296
- end
297
-
298
- # Create a new instance that communicates with the running pageant
299
- # instance. If no such instance is running, this will cause an error.
300
- def initialize
301
- @win = Win.FindWindow("Pageant", "Pageant")
302
-
303
- if @win == 0
304
- raise Net::SSH::Exception,
305
- "pageant process not running"
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
306
386
  end
307
387
 
308
- @input_buffer = Net::SSH::Buffer.new
309
- @output_buffer = Net::SSH::Buffer.new
310
- end
311
-
312
- # Forwards the data to #send_query, ignoring any arguments after
313
- # the first.
314
- def send(data, *args)
315
- @input_buffer.append(data)
316
-
317
- ret = data.length
318
-
319
- while true
320
- return ret if @input_buffer.length < 4
321
- msg_length = @input_buffer.read_long + 4
322
- @input_buffer.reset!
323
-
324
- return ret if @input_buffer.length < msg_length
325
- msg = @input_buffer.read!(msg_length)
326
- @output_buffer.append(send_query(msg))
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
327
493
  end
328
494
  end
329
-
330
- # Reads +n+ bytes from the cached result of the last query. If +n+
331
- # is +nil+, returns all remaining data from the last query.
332
- def read(n = nil)
333
- @output_buffer.read(n)
334
- end
335
-
336
- def close
337
- end
338
-
339
- # Packages the given query string and sends it to the pageant
340
- # process via the Windows messaging subsystem. The result is
341
- # cached, to be returned piece-wise when #read is called.
342
- def send_query(query)
343
- res = nil
344
- filemap = 0
345
- ptr = nil
346
- id = Win.malloc_ptr(Win::SIZEOF_DWORD)
347
-
348
- mapname = "PageantRequest%08x" % Win.GetCurrentThreadId()
349
- security_attributes = Win.get_ptr Win.get_security_attributes_for_user
350
-
351
- filemap = Win.CreateFileMapping(Win::INVALID_HANDLE_VALUE,
352
- security_attributes,
353
- Win::PAGE_READWRITE, 0,
354
- AGENT_MAX_MSGLEN, mapname)
355
-
356
- if filemap == 0 || filemap == Win::INVALID_HANDLE_VALUE
357
- raise Net::SSH::Exception,
358
- "Creation of file mapping failed with error: #{Win.GetLastError}"
359
- end
360
-
361
- ptr = Win.MapViewOfFile(filemap, Win::FILE_MAP_WRITE, 0, 0,
362
- 0)
363
-
364
- if ptr.nil? || ptr.null?
365
- raise Net::SSH::Exception, "Mapping of file failed"
366
- end
367
-
368
- Win.set_ptr_data(ptr, query)
369
-
370
- # using struct to achieve proper alignment and field size on 64-bit platform
371
- cds = Win::COPYDATASTRUCT.new(Win.malloc_ptr(Win::COPYDATASTRUCT.size))
372
- cds.dwData = AGENT_COPYDATA_ID
373
- cds.cbData = mapname.size + 1
374
- cds.lpData = Win.get_cstr(mapname)
375
- succ = Win.SendMessageTimeout(@win, Win::WM_COPYDATA, Win::NULL,
376
- cds.to_ptr, Win::SMTO_NORMAL, 5000, id)
377
-
378
- if succ > 0
379
- retlen = 4 + ptr.to_s(4).unpack("N")[0]
380
- res = ptr.to_s(retlen)
381
- else
382
- raise Net::SSH::Exception, "Message failed with error: #{Win.GetLastError}"
383
- end
384
-
385
- return res
386
- ensure
387
- Win.UnmapViewOfFile(ptr) unless ptr.nil? || ptr.null?
388
- Win.CloseHandle(filemap) if filemap != 0
389
- end
390
495
  end
391
496
  end
392
-
393
- end; end; end
497
+ end