net-ssh 2.7.0 → 7.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (199) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data/.dockerignore +6 -0
  4. data/.github/FUNDING.yml +1 -0
  5. data/.github/config/rubocop_linter_action.yml +4 -0
  6. data/.github/workflows/ci-with-docker.yml +44 -0
  7. data/.github/workflows/ci.yml +94 -0
  8. data/.github/workflows/rubocop.yml +16 -0
  9. data/.gitignore +15 -0
  10. data/.rubocop.yml +22 -0
  11. data/.rubocop_todo.yml +1081 -0
  12. data/CHANGES.txt +387 -0
  13. data/DEVELOPMENT.md +23 -0
  14. data/Dockerfile +29 -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 +303 -0
  22. data/Rakefile +174 -40
  23. data/SECURITY.md +4 -0
  24. data/THANKS.txt +25 -0
  25. data/appveyor.yml +58 -0
  26. data/docker-compose.yml +25 -0
  27. data/lib/net/ssh/authentication/agent.rb +279 -18
  28. data/lib/net/ssh/authentication/certificate.rb +183 -0
  29. data/lib/net/ssh/authentication/constants.rb +17 -15
  30. data/lib/net/ssh/authentication/ed25519.rb +184 -0
  31. data/lib/net/ssh/authentication/ed25519_loader.rb +31 -0
  32. data/lib/net/ssh/authentication/key_manager.rb +125 -54
  33. data/lib/net/ssh/authentication/methods/abstract.rb +67 -48
  34. data/lib/net/ssh/authentication/methods/hostbased.rb +34 -37
  35. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +19 -12
  36. data/lib/net/ssh/authentication/methods/none.rb +16 -19
  37. data/lib/net/ssh/authentication/methods/password.rb +56 -19
  38. data/lib/net/ssh/authentication/methods/publickey.rb +96 -55
  39. data/lib/net/ssh/authentication/pageant.rb +483 -246
  40. data/lib/net/ssh/authentication/pub_key_fingerprint.rb +43 -0
  41. data/lib/net/ssh/authentication/session.rb +138 -120
  42. data/lib/net/ssh/buffer.rb +399 -300
  43. data/lib/net/ssh/buffered_io.rb +154 -150
  44. data/lib/net/ssh/config.rb +361 -166
  45. data/lib/net/ssh/connection/channel.rb +640 -596
  46. data/lib/net/ssh/connection/constants.rb +29 -29
  47. data/lib/net/ssh/connection/event_loop.rb +123 -0
  48. data/lib/net/ssh/connection/keepalive.rb +59 -0
  49. data/lib/net/ssh/connection/session.rb +628 -548
  50. data/lib/net/ssh/connection/term.rb +125 -123
  51. data/lib/net/ssh/errors.rb +101 -95
  52. data/lib/net/ssh/key_factory.rb +198 -100
  53. data/lib/net/ssh/known_hosts.rb +221 -98
  54. data/lib/net/ssh/loggable.rb +50 -49
  55. data/lib/net/ssh/packet.rb +83 -79
  56. data/lib/net/ssh/prompt.rb +50 -81
  57. data/lib/net/ssh/proxy/command.rb +108 -60
  58. data/lib/net/ssh/proxy/errors.rb +12 -10
  59. data/lib/net/ssh/proxy/http.rb +82 -78
  60. data/lib/net/ssh/proxy/https.rb +50 -0
  61. data/lib/net/ssh/proxy/jump.rb +54 -0
  62. data/lib/net/ssh/proxy/socks4.rb +5 -8
  63. data/lib/net/ssh/proxy/socks5.rb +18 -20
  64. data/lib/net/ssh/service/forward.rb +383 -255
  65. data/lib/net/ssh/test/channel.rb +145 -136
  66. data/lib/net/ssh/test/extensions.rb +131 -110
  67. data/lib/net/ssh/test/kex.rb +34 -32
  68. data/lib/net/ssh/test/local_packet.rb +46 -44
  69. data/lib/net/ssh/test/packet.rb +89 -70
  70. data/lib/net/ssh/test/remote_packet.rb +32 -30
  71. data/lib/net/ssh/test/script.rb +156 -142
  72. data/lib/net/ssh/test/socket.rb +49 -48
  73. data/lib/net/ssh/test.rb +82 -77
  74. data/lib/net/ssh/transport/aes128_gcm.rb +40 -0
  75. data/lib/net/ssh/transport/aes256_gcm.rb +40 -0
  76. data/lib/net/ssh/transport/algorithms.rb +472 -348
  77. data/lib/net/ssh/transport/chacha20_poly1305_cipher.rb +117 -0
  78. data/lib/net/ssh/transport/chacha20_poly1305_cipher_loader.rb +17 -0
  79. data/lib/net/ssh/transport/cipher_factory.rb +124 -100
  80. data/lib/net/ssh/transport/constants.rb +32 -24
  81. data/lib/net/ssh/transport/ctr.rb +42 -22
  82. data/lib/net/ssh/transport/gcm_cipher.rb +207 -0
  83. data/lib/net/ssh/transport/hmac/abstract.rb +97 -63
  84. data/lib/net/ssh/transport/hmac/md5.rb +0 -2
  85. data/lib/net/ssh/transport/hmac/md5_96.rb +0 -2
  86. data/lib/net/ssh/transport/hmac/none.rb +0 -2
  87. data/lib/net/ssh/transport/hmac/ripemd160.rb +0 -2
  88. data/lib/net/ssh/transport/hmac/sha1.rb +0 -2
  89. data/lib/net/ssh/transport/hmac/sha1_96.rb +0 -2
  90. data/lib/net/ssh/transport/hmac/sha2_256.rb +7 -11
  91. data/lib/net/ssh/transport/hmac/sha2_256_96.rb +4 -8
  92. data/lib/net/ssh/transport/hmac/sha2_256_etm.rb +12 -0
  93. data/lib/net/ssh/transport/hmac/sha2_512.rb +6 -9
  94. data/lib/net/ssh/transport/hmac/sha2_512_96.rb +4 -8
  95. data/lib/net/ssh/transport/hmac/sha2_512_etm.rb +12 -0
  96. data/lib/net/ssh/transport/hmac.rb +14 -12
  97. data/lib/net/ssh/transport/identity_cipher.rb +54 -44
  98. data/lib/net/ssh/transport/kex/abstract.rb +130 -0
  99. data/lib/net/ssh/transport/kex/abstract5656.rb +72 -0
  100. data/lib/net/ssh/transport/kex/curve25519_sha256.rb +39 -0
  101. data/lib/net/ssh/transport/kex/curve25519_sha256_loader.rb +30 -0
  102. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +33 -40
  103. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha256.rb +11 -0
  104. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +119 -213
  105. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +53 -61
  106. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha256.rb +5 -9
  107. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +36 -90
  108. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +18 -10
  109. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +18 -10
  110. data/lib/net/ssh/transport/kex.rb +15 -12
  111. data/lib/net/ssh/transport/key_expander.rb +24 -20
  112. data/lib/net/ssh/transport/openssl.rb +161 -124
  113. data/lib/net/ssh/transport/openssl_cipher_extensions.rb +8 -0
  114. data/lib/net/ssh/transport/packet_stream.rb +246 -183
  115. data/lib/net/ssh/transport/server_version.rb +57 -51
  116. data/lib/net/ssh/transport/session.rb +307 -235
  117. data/lib/net/ssh/transport/state.rb +178 -176
  118. data/lib/net/ssh/verifiers/accept_new.rb +33 -0
  119. data/lib/net/ssh/verifiers/accept_new_or_local_tunnel.rb +33 -0
  120. data/lib/net/ssh/verifiers/always.rb +58 -0
  121. data/lib/net/ssh/verifiers/never.rb +19 -0
  122. data/lib/net/ssh/version.rb +57 -51
  123. data/lib/net/ssh.rb +140 -40
  124. data/net-ssh-public_cert.pem +21 -0
  125. data/net-ssh.gemspec +39 -184
  126. data/support/ssh_tunnel_bug.rb +5 -5
  127. data.tar.gz.sig +0 -0
  128. metadata +205 -99
  129. metadata.gz.sig +0 -0
  130. data/README.rdoc +0 -219
  131. data/Rudyfile +0 -96
  132. data/gem-public_cert.pem +0 -20
  133. data/lib/net/ssh/authentication/agent/java_pageant.rb +0 -85
  134. data/lib/net/ssh/authentication/agent/socket.rb +0 -170
  135. data/lib/net/ssh/ruby_compat.rb +0 -51
  136. data/lib/net/ssh/verifiers/lenient.rb +0 -30
  137. data/lib/net/ssh/verifiers/null.rb +0 -12
  138. data/lib/net/ssh/verifiers/secure.rb +0 -54
  139. data/lib/net/ssh/verifiers/strict.rb +0 -24
  140. data/setup.rb +0 -1585
  141. data/support/arcfour_check.rb +0 -20
  142. data/test/README.txt +0 -47
  143. data/test/authentication/methods/common.rb +0 -28
  144. data/test/authentication/methods/test_abstract.rb +0 -51
  145. data/test/authentication/methods/test_hostbased.rb +0 -114
  146. data/test/authentication/methods/test_keyboard_interactive.rb +0 -100
  147. data/test/authentication/methods/test_none.rb +0 -41
  148. data/test/authentication/methods/test_password.rb +0 -52
  149. data/test/authentication/methods/test_publickey.rb +0 -148
  150. data/test/authentication/test_agent.rb +0 -205
  151. data/test/authentication/test_key_manager.rb +0 -218
  152. data/test/authentication/test_session.rb +0 -108
  153. data/test/common.rb +0 -108
  154. data/test/configs/eqsign +0 -3
  155. data/test/configs/exact_match +0 -8
  156. data/test/configs/host_plus +0 -10
  157. data/test/configs/multihost +0 -4
  158. data/test/configs/nohost +0 -19
  159. data/test/configs/numeric_host +0 -4
  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 -467
  164. data/test/connection/test_session.rb +0 -526
  165. data/test/known_hosts/github +0 -1
  166. data/test/manual/test_forward.rb +0 -223
  167. data/test/start/test_options.rb +0 -36
  168. data/test/start/test_transport.rb +0 -28
  169. data/test/test_all.rb +0 -11
  170. data/test/test_buffer.rb +0 -433
  171. data/test/test_buffered_io.rb +0 -63
  172. data/test/test_config.rb +0 -151
  173. data/test/test_key_factory.rb +0 -173
  174. data/test/test_known_hosts.rb +0 -13
  175. data/test/transport/hmac/test_md5.rb +0 -41
  176. data/test/transport/hmac/test_md5_96.rb +0 -27
  177. data/test/transport/hmac/test_none.rb +0 -34
  178. data/test/transport/hmac/test_ripemd160.rb +0 -36
  179. data/test/transport/hmac/test_sha1.rb +0 -36
  180. data/test/transport/hmac/test_sha1_96.rb +0 -27
  181. data/test/transport/hmac/test_sha2_256.rb +0 -37
  182. data/test/transport/hmac/test_sha2_256_96.rb +0 -27
  183. data/test/transport/hmac/test_sha2_512.rb +0 -37
  184. data/test/transport/hmac/test_sha2_512_96.rb +0 -27
  185. data/test/transport/kex/test_diffie_hellman_group14_sha1.rb +0 -13
  186. data/test/transport/kex/test_diffie_hellman_group1_sha1.rb +0 -146
  187. data/test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb +0 -92
  188. data/test/transport/kex/test_diffie_hellman_group_exchange_sha256.rb +0 -34
  189. data/test/transport/kex/test_ecdh_sha2_nistp256.rb +0 -161
  190. data/test/transport/kex/test_ecdh_sha2_nistp384.rb +0 -38
  191. data/test/transport/kex/test_ecdh_sha2_nistp521.rb +0 -38
  192. data/test/transport/test_algorithms.rb +0 -330
  193. data/test/transport/test_cipher_factory.rb +0 -443
  194. data/test/transport/test_hmac.rb +0 -34
  195. data/test/transport/test_identity_cipher.rb +0 -40
  196. data/test/transport/test_packet_stream.rb +0 -1755
  197. data/test/transport/test_server_version.rb +0 -78
  198. data/test/transport/test_session.rb +0 -319
  199. data/test/transport/test_state.rb +0 -181
@@ -1,260 +1,497 @@
1
- require 'dl/import'
2
-
3
1
  if RUBY_VERSION < "1.9"
2
+ require 'dl/import'
4
3
  require 'dl/struct'
5
- else
4
+ elsif RUBY_VERSION < "2.1"
5
+ require 'dl/import'
6
6
  require 'dl/types'
7
7
  require 'dl'
8
+ else
9
+ require 'fiddle'
10
+ require 'fiddle/types'
11
+ require 'fiddle/import'
12
+
13
+ # For now map DL to Fiddler versus updating all the code below
14
+ module DL
15
+ CPtr ||= Fiddle::Pointer
16
+ if RUBY_PLATFORM != "java"
17
+ RUBY_FREE ||= Fiddle::RUBY_FREE
18
+ end
19
+ end
8
20
  end
9
21
 
10
22
  require 'net/ssh/errors'
11
23
 
12
- module Net; module SSH; module Authentication
13
-
14
- # This module encapsulates the implementation of a socket factory that
15
- # uses the PuTTY "pageant" utility to obtain information about SSH
16
- # identities.
17
- #
18
- # This code is a slightly modified version of the original implementation
19
- # by Guillaume Marçais (guillaume.marcais@free.fr). It is used and
20
- # relicensed by permission.
21
- module Pageant
22
-
23
- # From Putty pageant.c
24
- AGENT_MAX_MSGLEN = 8192
25
- AGENT_COPYDATA_ID = 0x804e50ba
26
-
27
- # The definition of the Windows methods and data structures used in
28
- # communicating with the pageant process.
29
- module Win
30
- if RUBY_VERSION < "1.9"
31
- extend DL::Importable
32
-
33
- dlload 'user32'
34
- dlload 'kernel32'
35
- else
36
- extend DL::Importer
37
- dlload 'user32','kernel32'
38
- include DL::Win32Types
39
- end
40
-
41
- typealias("LPCTSTR", "char *") # From winnt.h
42
- typealias("LPVOID", "void *") # From winnt.h
43
- typealias("LPCVOID", "const void *") # From windef.h
44
- typealias("LRESULT", "long") # From windef.h
45
- typealias("WPARAM", "unsigned int *") # From windef.h
46
- typealias("LPARAM", "long *") # From windef.h
47
- typealias("PDWORD_PTR", "long *") # From basetsd.h
48
-
49
- # From winbase.h, winnt.h
50
- INVALID_HANDLE_VALUE = -1
51
- NULL = nil
52
- PAGE_READWRITE = 0x0004
53
- FILE_MAP_WRITE = 2
54
- WM_COPYDATA = 74
55
-
56
- SMTO_NORMAL = 0 # From winuser.h
57
-
58
- # args: lpClassName, lpWindowName
59
- extern 'HWND FindWindow(LPCTSTR, LPCTSTR)'
60
-
61
- # args: none
62
- extern 'DWORD GetCurrentThreadId()'
63
-
64
- # args: hFile, (ignored), flProtect, dwMaximumSizeHigh,
65
- # dwMaximumSizeLow, lpName
66
- extern 'HANDLE CreateFileMapping(HANDLE, void *, DWORD, DWORD, ' +
67
- 'DWORD, LPCTSTR)'
68
-
69
- # args: hFileMappingObject, dwDesiredAccess, dwFileOffsetHigh,
70
- # dwfileOffsetLow, dwNumberOfBytesToMap
71
- extern 'LPVOID MapViewOfFile(HANDLE, DWORD, DWORD, DWORD, DWORD)'
72
-
73
- # args: lpBaseAddress
74
- extern 'BOOL UnmapViewOfFile(LPCVOID)'
75
-
76
- # args: hObject
77
- extern 'BOOL CloseHandle(HANDLE)'
78
-
79
- # args: hWnd, Msg, wParam, lParam, fuFlags, uTimeout, lpdwResult
80
- extern 'LRESULT SendMessageTimeout(HWND, UINT, WPARAM, LPARAM, ' +
81
- 'UINT, UINT, PDWORD_PTR)'
82
- if RUBY_VERSION < "1.9"
83
- alias_method :FindWindow,:findWindow
84
- module_function :FindWindow
85
- end
86
- end
87
-
88
- # This is the pseudo-socket implementation that mimics the interface of
89
- # a socket, translating each request into a Windows messaging call to
90
- # the pageant daemon. This allows pageant support to be implemented
91
- # simply by replacing the socket factory used by the Agent class.
92
- class Socket
93
-
94
- private_class_method :new
95
-
96
- # The factory method for creating a new Socket instance. The location
97
- # parameter is ignored, and is only needed for compatibility with
98
- # the general Socket interface.
99
- def self.open(location=nil)
100
- new
101
- end
102
-
103
- # Create a new instance that communicates with the running pageant
104
- # instance. If no such instance is running, this will cause an error.
105
- def initialize
106
- @win = Win.FindWindow("Pageant", "Pageant")
107
-
108
- if @win == 0
109
- raise Net::SSH::Exception,
110
- "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
111
386
  end
112
387
 
113
- @res = nil
114
- @pos = 0
115
- end
116
-
117
- # Forwards the data to #send_query, ignoring any arguments after
118
- # the first. Returns 0.
119
- def send(data, *args)
120
- @res = send_query(data)
121
- @pos = 0
122
- end
123
-
124
- # Packages the given query string and sends it to the pageant
125
- # process via the Windows messaging subsystem. The result is
126
- # cached, to be returned piece-wise when #read is called.
127
- def send_query(query)
128
- res = nil
129
- filemap = 0
130
- ptr = nil
131
- id = DL::PtrData.malloc(DL.sizeof("L"))
132
-
133
- mapname = "PageantRequest%08x\000" % Win.getCurrentThreadId()
134
- filemap = Win.createFileMapping(Win::INVALID_HANDLE_VALUE,
135
- Win::NULL,
136
- Win::PAGE_READWRITE, 0,
137
- AGENT_MAX_MSGLEN, mapname)
138
- if filemap == 0
139
- raise Net::SSH::Exception,
140
- "Creation of file mapping failed"
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
141
493
  end
142
-
143
- ptr = Win.mapViewOfFile(filemap, Win::FILE_MAP_WRITE, 0, 0,
144
- AGENT_MAX_MSGLEN)
145
-
146
- if ptr.nil? || ptr.null?
147
- raise Net::SSH::Exception, "Mapping of file failed"
148
- end
149
-
150
- ptr[0] = query
151
-
152
- cds = [AGENT_COPYDATA_ID, mapname.size + 1, mapname].
153
- pack("LLp").to_ptr
154
- succ = Win.sendMessageTimeout(@win, Win::WM_COPYDATA, Win::NULL,
155
- cds, Win::SMTO_NORMAL, 5000, id)
156
-
157
- if succ > 0
158
- retlen = 4 + ptr.to_s(4).unpack("N")[0]
159
- res = ptr.to_s(retlen)
160
- end
161
-
162
- return res
163
- ensure
164
- Win.unmapViewOfFile(ptr) unless ptr.nil? || ptr.null?
165
- Win.closeHandle(filemap) if filemap != 0
166
494
  end
167
-
168
- # Conceptually close the socket. This doesn't really do anthing
169
- # significant, but merely complies with the Socket interface.
170
- def close
171
- @res = nil
172
- @pos = 0
173
- end
174
-
175
- # Conceptually asks if the socket is closed. As with #close,
176
- # this doesn't really do anything significant, but merely
177
- # complies with the Socket interface.
178
- def closed?
179
- @res.nil? && @pos.zero?
180
- end
181
-
182
- # Reads +n+ bytes from the cached result of the last query. If +n+
183
- # is +nil+, returns all remaining data from the last query.
184
- def read(n = nil)
185
- return nil unless @res
186
- if n.nil?
187
- start, @pos = @pos, @res.size
188
- return @res[start..-1]
189
- else
190
- start, @pos = @pos, @pos + n
191
- return @res[start, n]
192
- end
193
- end
194
-
195
495
  end
196
-
197
- # Socket changes for Ruby 1.9
198
- # Functionality is the same as Ruby 1.8 but it includes the new calls to
199
- # the DL module as well as other pointer transformations
200
- class Socket19 < Socket
201
- # Packages the given query string and sends it to the pageant
202
- # process via the Windows messaging subsystem. The result is
203
- # cached, to be returned piece-wise when #read is called.
204
- def send_query(query)
205
- res = nil
206
- filemap = 0
207
- ptr = nil
208
- id = DL.malloc(DL::SIZEOF_LONG)
209
-
210
- mapname = "PageantRequest%08x\000" % Win.GetCurrentThreadId()
211
-
212
- filemap = Win.CreateFileMapping(Win::INVALID_HANDLE_VALUE,
213
- Win::NULL,
214
- Win::PAGE_READWRITE, 0,
215
- AGENT_MAX_MSGLEN, mapname)
216
-
217
- if filemap == 0 || filemap == Win::INVALID_HANDLE_VALUE
218
- raise Net::SSH::Exception,
219
- "Creation of file mapping failed"
220
- end
221
-
222
- ptr = Win.MapViewOfFile(filemap, Win::FILE_MAP_WRITE, 0, 0,
223
- 0)
224
-
225
- if ptr.nil? || ptr.null?
226
- raise Net::SSH::Exception, "Mapping of file failed"
227
- end
228
-
229
- DL::CPtr.new(ptr)[0,query.size]=query
230
-
231
- cds = DL::CPtr.to_ptr [AGENT_COPYDATA_ID, mapname.size + 1, mapname].
232
- pack("LLp")
233
- succ = Win.SendMessageTimeout(@win, Win::WM_COPYDATA, Win::NULL,
234
- cds, Win::SMTO_NORMAL, 5000, id)
235
-
236
- if succ > 0
237
- retlen = 4 + ptr.to_s(4).unpack("N")[0]
238
- res = ptr.to_s(retlen)
239
- end
240
-
241
- return res
242
- ensure
243
- Win.UnmapViewOfFile(ptr) unless ptr.nil? || ptr.null?
244
- Win.CloseHandle(filemap) if filemap != 0
245
- end
246
- end
247
-
248
- # Selects which socket to use depending on the ruby version
249
- # This is needed due changes in the DL module.
250
- def self.socket_factory
251
- if RUBY_VERSION < "1.9"
252
- Socket
253
- else
254
- Socket19
255
- end
256
- end
257
-
258
496
  end
259
-
260
- end; end; end
497
+ end