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