PageRenderTime 49ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/bundle/ruby/2.0.0/gems/net-ssh-2.9.1/lib/net/ssh/authentication/pageant.rb

https://gitlab.com/slice-group/creators
Ruby | 373 lines | 233 code | 75 blank | 65 comment | 20 complexity | e25146eb84fb974f47fd24bc1a00fd8a MD5 | raw file
Possible License(s): GPL-3.0, Apache-2.0, LGPL-2.1, JSON, MIT, BSD-2-Clause, BSD-3-Clause, GPL-2.0, MPL-2.0-no-copyleft-exception
  1. require 'dl/import'
  2. if RUBY_VERSION < "1.9"
  3. require 'dl/struct'
  4. else
  5. require 'dl/types'
  6. require 'dl'
  7. end
  8. require 'net/ssh/errors'
  9. module Net; module SSH; module Authentication
  10. # This module encapsulates the implementation of a socket factory that
  11. # uses the PuTTY "pageant" utility to obtain information about SSH
  12. # identities.
  13. #
  14. # This code is a slightly modified version of the original implementation
  15. # by Guillaume Marçais (guillaume.marcais@free.fr). It is used and
  16. # relicensed by permission.
  17. module Pageant
  18. # From Putty pageant.c
  19. AGENT_MAX_MSGLEN = 8192
  20. AGENT_COPYDATA_ID = 0x804e50ba
  21. # The definition of the Windows methods and data structures used in
  22. # communicating with the pageant process.
  23. module Win
  24. # Compatibility on initialization
  25. if RUBY_VERSION < "1.9"
  26. extend DL::Importable
  27. dlload 'user32'
  28. dlload 'kernel32'
  29. dlload 'advapi32'
  30. SIZEOF_DWORD = DL.sizeof('L')
  31. else
  32. extend DL::Importer
  33. dlload 'user32','kernel32', 'advapi32'
  34. include DL::Win32Types
  35. SIZEOF_DWORD = DL::SIZEOF_LONG
  36. end
  37. typealias("LPCTSTR", "char *") # From winnt.h
  38. typealias("LPVOID", "void *") # From winnt.h
  39. typealias("LPCVOID", "const void *") # From windef.h
  40. typealias("LRESULT", "long") # From windef.h
  41. typealias("WPARAM", "unsigned int *") # From windef.h
  42. typealias("LPARAM", "long *") # From windef.h
  43. typealias("PDWORD_PTR", "long *") # From basetsd.h
  44. typealias("USHORT", "unsigned short") # From windef.h
  45. # From winbase.h, winnt.h
  46. INVALID_HANDLE_VALUE = -1
  47. NULL = nil
  48. PAGE_READWRITE = 0x0004
  49. FILE_MAP_WRITE = 2
  50. WM_COPYDATA = 74
  51. SMTO_NORMAL = 0 # From winuser.h
  52. # args: lpClassName, lpWindowName
  53. extern 'HWND FindWindow(LPCTSTR, LPCTSTR)'
  54. # args: none
  55. extern 'DWORD GetCurrentThreadId()'
  56. # args: hFile, (ignored), flProtect, dwMaximumSizeHigh,
  57. # dwMaximumSizeLow, lpName
  58. extern 'HANDLE CreateFileMapping(HANDLE, void *, DWORD, ' +
  59. 'DWORD, DWORD, LPCTSTR)'
  60. # args: hFileMappingObject, dwDesiredAccess, dwFileOffsetHigh,
  61. # dwfileOffsetLow, dwNumberOfBytesToMap
  62. extern 'LPVOID MapViewOfFile(HANDLE, DWORD, DWORD, DWORD, DWORD)'
  63. # args: lpBaseAddress
  64. extern 'BOOL UnmapViewOfFile(LPCVOID)'
  65. # args: hObject
  66. extern 'BOOL CloseHandle(HANDLE)'
  67. # args: hWnd, Msg, wParam, lParam, fuFlags, uTimeout, lpdwResult
  68. extern 'LRESULT SendMessageTimeout(HWND, UINT, WPARAM, LPARAM, ' +
  69. 'UINT, UINT, PDWORD_PTR)'
  70. # args: none
  71. extern 'DWORD GetLastError()'
  72. # args: none
  73. extern 'HANDLE GetCurrentProcess()'
  74. # args: hProcessHandle, dwDesiredAccess, (out) phNewTokenHandle
  75. extern 'BOOL OpenProcessToken(HANDLE, DWORD, PHANDLE)'
  76. # args: hTokenHandle, uTokenInformationClass,
  77. # (out) lpTokenInformation, dwTokenInformationLength
  78. # (out) pdwInfoReturnLength
  79. extern 'BOOL GetTokenInformation(HANDLE, UINT, LPVOID, DWORD, ' +
  80. 'PDWORD)'
  81. # args: (out) lpSecurityDescriptor, dwRevisionLevel
  82. extern 'BOOL InitializeSecurityDescriptor(LPVOID, DWORD)'
  83. # args: (out) lpSecurityDescriptor, lpOwnerSid, bOwnerDefaulted
  84. extern 'BOOL SetSecurityDescriptorOwner(LPVOID, LPVOID, BOOL)'
  85. # args: pSecurityDescriptor
  86. extern 'BOOL IsValidSecurityDescriptor(LPVOID)'
  87. # Constants needed for security attribute retrieval.
  88. # Specifies the access mask corresponding to the desired access
  89. # rights.
  90. TOKEN_QUERY = 0x8
  91. # The value of TOKEN_USER from the TOKEN_INFORMATION_CLASS enum.
  92. TOKEN_USER_INFORMATION_CLASS = 1
  93. # The initial revision level assigned to the security descriptor.
  94. REVISION = 1
  95. # Structs for security attribute functions.
  96. # Holds the retrieved user access token.
  97. TOKEN_USER = struct ['void * SID', 'DWORD ATTRIBUTES']
  98. # Contains the security descriptor, this gets passed to the
  99. # function that constructs the shared memory map.
  100. SECURITY_ATTRIBUTES = struct ['DWORD nLength',
  101. 'LPVOID lpSecurityDescriptor',
  102. 'BOOL bInheritHandle']
  103. # The security descriptor holds security information.
  104. SECURITY_DESCRIPTOR = struct ['UCHAR Revision', 'UCHAR Sbz1',
  105. 'USHORT Control', 'LPVOID Owner',
  106. 'LPVOID Group', 'LPVOID Sacl',
  107. 'LPVOID Dacl']
  108. # Compatibility for security attribute retrieval.
  109. if RUBY_VERSION < "1.9"
  110. # Alias functions to > 1.9 capitalization
  111. %w(findWindow
  112. getCurrentProcess
  113. initializeSecurityDescriptor
  114. setSecurityDescriptorOwner
  115. isValidSecurityDescriptor
  116. openProcessToken
  117. getTokenInformation
  118. getLastError
  119. getCurrentThreadId
  120. createFileMapping
  121. mapViewOfFile
  122. sendMessageTimeout
  123. unmapViewOfFile
  124. closeHandle).each do |name|
  125. new_name = name[0].chr.upcase + name[1..name.length]
  126. alias_method new_name, name
  127. module_function new_name
  128. end
  129. def self.malloc_ptr(size)
  130. return DL.malloc(size)
  131. end
  132. def self.get_ptr(data)
  133. return data.to_ptr
  134. end
  135. def self.set_ptr_data(ptr, data)
  136. ptr[0] = data
  137. end
  138. else
  139. def self.malloc_ptr(size)
  140. return DL::CPtr.malloc(size, DL::RUBY_FREE)
  141. end
  142. def self.get_ptr(data)
  143. return DL::CPtr.to_ptr data
  144. end
  145. def self.set_ptr_data(ptr, data)
  146. DL::CPtr.new(ptr)[0,data.size] = data
  147. end
  148. end
  149. def self.get_security_attributes_for_user
  150. user = get_current_user
  151. psd_information = malloc_ptr(Win::SECURITY_DESCRIPTOR.size)
  152. raise_error_if_zero(
  153. Win.InitializeSecurityDescriptor(psd_information,
  154. Win::REVISION))
  155. raise_error_if_zero(
  156. Win.SetSecurityDescriptorOwner(psd_information, user.SID,
  157. 0))
  158. raise_error_if_zero(
  159. Win.IsValidSecurityDescriptor(psd_information))
  160. nLength = Win::SECURITY_ATTRIBUTES.size
  161. lpSecurityDescriptor = psd_information
  162. bInheritHandle = 1
  163. sa = [nLength, lpSecurityDescriptor.to_i,
  164. bInheritHandle].pack("LLC")
  165. return sa
  166. end
  167. def self.get_current_user
  168. token_handle = open_process_token(Win.GetCurrentProcess,
  169. Win::TOKEN_QUERY)
  170. token_user = get_token_information(token_handle,
  171. Win::TOKEN_USER_INFORMATION_CLASS)
  172. return token_user
  173. end
  174. def self.open_process_token(process_handle, desired_access)
  175. ptoken_handle = malloc_ptr(Win::SIZEOF_DWORD)
  176. raise_error_if_zero(
  177. Win.OpenProcessToken(process_handle, desired_access,
  178. ptoken_handle))
  179. token_handle = ptoken_handle.ptr.to_i
  180. return token_handle
  181. end
  182. def self.get_token_information(token_handle,
  183. token_information_class)
  184. # Hold the size of the information to be returned
  185. preturn_length = malloc_ptr(Win::SIZEOF_DWORD)
  186. # Going to throw an INSUFFICIENT_BUFFER_ERROR, but that is ok
  187. # here. This is retrieving the size of the information to be
  188. # returned.
  189. Win.GetTokenInformation(token_handle,
  190. token_information_class,
  191. Win::NULL, 0, preturn_length)
  192. ptoken_information = malloc_ptr(preturn_length.ptr.to_i)
  193. # This call is going to write the requested information to
  194. # the memory location referenced by token_information.
  195. raise_error_if_zero(
  196. Win.GetTokenInformation(token_handle,
  197. token_information_class,
  198. ptoken_information,
  199. ptoken_information.size,
  200. preturn_length))
  201. return TOKEN_USER.new(ptoken_information)
  202. end
  203. def self.raise_error_if_zero(result)
  204. if result == 0
  205. raise "Windows error: #{Win.GetLastError}"
  206. end
  207. end
  208. # Get a null-terminated string given a string.
  209. def self.get_cstr(str)
  210. return str + "\000"
  211. end
  212. end
  213. # This is the pseudo-socket implementation that mimics the interface of
  214. # a socket, translating each request into a Windows messaging call to
  215. # the pageant daemon. This allows pageant support to be implemented
  216. # simply by replacing the socket factory used by the Agent class.
  217. class Socket
  218. private_class_method :new
  219. # The factory method for creating a new Socket instance. The location
  220. # parameter is ignored, and is only needed for compatibility with
  221. # the general Socket interface.
  222. def self.open(location=nil)
  223. new
  224. end
  225. # Create a new instance that communicates with the running pageant
  226. # instance. If no such instance is running, this will cause an error.
  227. def initialize
  228. @win = Win.FindWindow("Pageant", "Pageant")
  229. if @win == 0
  230. raise Net::SSH::Exception,
  231. "pageant process not running"
  232. end
  233. @input_buffer = Net::SSH::Buffer.new
  234. @output_buffer = Net::SSH::Buffer.new
  235. end
  236. # Forwards the data to #send_query, ignoring any arguments after
  237. # the first.
  238. def send(data, *args)
  239. @input_buffer.append(data)
  240. ret = data.length
  241. while true
  242. return ret if @input_buffer.length < 4
  243. msg_length = @input_buffer.read_long + 4
  244. @input_buffer.reset!
  245. return ret if @input_buffer.length < msg_length
  246. msg = @input_buffer.read!(msg_length)
  247. @output_buffer.append(send_query(msg))
  248. end
  249. end
  250. # Reads +n+ bytes from the cached result of the last query. If +n+
  251. # is +nil+, returns all remaining data from the last query.
  252. def read(n = nil)
  253. @output_buffer.read(n)
  254. end
  255. def close
  256. end
  257. # Packages the given query string and sends it to the pageant
  258. # process via the Windows messaging subsystem. The result is
  259. # cached, to be returned piece-wise when #read is called.
  260. def send_query(query)
  261. res = nil
  262. filemap = 0
  263. ptr = nil
  264. id = Win.malloc_ptr(Win::SIZEOF_DWORD)
  265. mapname = "PageantRequest%08x" % Win.GetCurrentThreadId()
  266. security_attributes = Win.get_ptr Win.get_security_attributes_for_user
  267. filemap = Win.CreateFileMapping(Win::INVALID_HANDLE_VALUE,
  268. security_attributes,
  269. Win::PAGE_READWRITE, 0,
  270. AGENT_MAX_MSGLEN, mapname)
  271. if filemap == 0 || filemap == Win::INVALID_HANDLE_VALUE
  272. raise Net::SSH::Exception,
  273. "Creation of file mapping failed with error: #{Win.GetLastError}"
  274. end
  275. ptr = Win.MapViewOfFile(filemap, Win::FILE_MAP_WRITE, 0, 0,
  276. 0)
  277. if ptr.nil? || ptr.null?
  278. raise Net::SSH::Exception, "Mapping of file failed"
  279. end
  280. Win.set_ptr_data(ptr, query)
  281. cds = Win.get_ptr [AGENT_COPYDATA_ID, mapname.size + 1,
  282. Win.get_cstr(mapname)].pack("LLp")
  283. succ = Win.SendMessageTimeout(@win, Win::WM_COPYDATA, Win::NULL,
  284. cds, Win::SMTO_NORMAL, 5000, id)
  285. if succ > 0
  286. retlen = 4 + ptr.to_s(4).unpack("N")[0]
  287. res = ptr.to_s(retlen)
  288. else
  289. raise Net::SSH::Exception, "Message failed with error: #{Win.GetLastError}"
  290. end
  291. return res
  292. ensure
  293. Win.UnmapViewOfFile(ptr) unless ptr.nil? || ptr.null?
  294. Win.CloseHandle(filemap) if filemap != 0
  295. end
  296. end
  297. end
  298. end; end; end