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
|
require_relative '../../../puppet/util/windows'
module Puppet::Util::Windows::SID
class Principal
extend FFI::Library
attr_reader :account, :sid_bytes, :sid, :domain, :domain_account, :account_type
def initialize(account, sid_bytes, sid, domain, account_type)
# This is only ever called from lookup_account_sid which has already
# removed the potential for passing in an account like host\user
@account = account
@sid_bytes = sid_bytes
@sid = sid
@domain = domain
@account_type = account_type
# When domain is available and it is a Domain principal, use domain only
# otherwise if domain is available then combine it with parsed account
# otherwise when the domain is not available, use the account value directly
# WinNT naming standard https://msdn.microsoft.com/en-us/library/windows/desktop/aa746534(v=vs.85).aspx
if (domain && !domain.empty? && @account_type == :SidTypeDomain)
@domain_account = @domain
elsif (domain && !domain.empty?)
@domain_account = "#{domain}\\#{@account}"
else
@domain_account = account
end
end
# added for backward compatibility
def ==(compare)
compare.is_a?(Puppet::Util::Windows::SID::Principal) &&
@sid_bytes == compare.sid_bytes
end
# returns authority qualified account name
# prefer to compare Principal instances with == operator or by #sid
def to_s
@domain_account
end
# = 8 + max sub identifiers (15) * 4
MAXIMUM_SID_BYTE_LENGTH = 68
ERROR_INVALID_PARAMETER = 87
ERROR_INSUFFICIENT_BUFFER = 122
def self.lookup_account_name(system_name = nil, sanitize = true, account_name)
account_name = sanitize_account_name(account_name) if sanitize
system_name_ptr = FFI::Pointer::NULL
begin
if system_name
system_name_wide = Puppet::Util::Windows::String.wide_string(system_name)
system_name_ptr = FFI::MemoryPointer.from_wide_string(system_name_wide)
end
FFI::MemoryPointer.from_string_to_wide_string(account_name) do |account_name_ptr|
FFI::MemoryPointer.new(:byte, MAXIMUM_SID_BYTE_LENGTH) do |sid_ptr|
FFI::MemoryPointer.new(:dword, 1) do |sid_length_ptr|
FFI::MemoryPointer.new(:dword, 1) do |domain_length_ptr|
FFI::MemoryPointer.new(:uint32, 1) do |name_use_enum_ptr|
sid_length_ptr.write_dword(MAXIMUM_SID_BYTE_LENGTH)
success = LookupAccountNameW(system_name_ptr, account_name_ptr, sid_ptr, sid_length_ptr,
FFI::Pointer::NULL, domain_length_ptr, name_use_enum_ptr)
last_error = FFI.errno
if (success == FFI::WIN32_FALSE && last_error != ERROR_INSUFFICIENT_BUFFER)
raise Puppet::Util::Windows::Error.new(_('Failed to call LookupAccountNameW with account: %{account_name}') % { account_name: account_name}, last_error)
end
FFI::MemoryPointer.new(:lpwstr, domain_length_ptr.read_dword) do |domain_ptr|
if LookupAccountNameW(system_name_ptr, account_name_ptr,
sid_ptr, sid_length_ptr,
domain_ptr, domain_length_ptr, name_use_enum_ptr) == FFI::WIN32_FALSE
raise Puppet::Util::Windows::Error.new(_('Failed to call LookupAccountNameW with account: %{account_name}') % { account_name: account_name} )
end
# with a SID returned, loop back through lookup_account_sid to retrieve official name
# necessary when accounts like . or '' are passed in
return lookup_account_sid(
system_name,
sid_ptr.read_bytes(sid_length_ptr.read_dword).unpack('C*'))
end
end
end
end
end
end
ensure
system_name_ptr.free if system_name_ptr != FFI::Pointer::NULL
end
end
def self.lookup_account_sid(system_name = nil, sid_bytes)
system_name_ptr = FFI::Pointer::NULL
if (sid_bytes.nil? || (!sid_bytes.is_a? Array) || (sid_bytes.length == 0))
#TRANSLATORS `lookup_account_sid` is a variable name and should not be translated
raise Puppet::Util::Windows::Error.new(_('Byte array for lookup_account_sid must not be nil and must be at least 1 byte long'))
end
begin
if system_name
system_name_wide = Puppet::Util::Windows::String.wide_string(system_name)
system_name_ptr = FFI::MemoryPointer.from_wide_string(system_name_wide)
end
FFI::MemoryPointer.new(:byte, sid_bytes.length) do |sid_ptr|
FFI::MemoryPointer.new(:dword, 1) do |name_length_ptr|
FFI::MemoryPointer.new(:dword, 1) do |domain_length_ptr|
FFI::MemoryPointer.new(:uint32, 1) do |name_use_enum_ptr|
sid_ptr.write_array_of_uchar(sid_bytes)
if Puppet::Util::Windows::SID.IsValidSid(sid_ptr) == FFI::WIN32_FALSE
raise Puppet::Util::Windows::Error.new(_('Byte array for lookup_account_sid is invalid: %{sid_bytes}') % { sid_bytes: sid_bytes }, ERROR_INVALID_PARAMETER)
end
success = LookupAccountSidW(system_name_ptr, sid_ptr, FFI::Pointer::NULL, name_length_ptr,
FFI::Pointer::NULL, domain_length_ptr, name_use_enum_ptr)
last_error = FFI.errno
if (success == FFI::WIN32_FALSE && last_error != ERROR_INSUFFICIENT_BUFFER)
raise Puppet::Util::Windows::Error.new(_('Failed to call LookupAccountSidW with bytes: %{sid_bytes}') % { sid_bytes: sid_bytes}, last_error)
end
FFI::MemoryPointer.new(:lpwstr, name_length_ptr.read_dword) do |name_ptr|
FFI::MemoryPointer.new(:lpwstr, domain_length_ptr.read_dword) do |domain_ptr|
if LookupAccountSidW(system_name_ptr, sid_ptr, name_ptr, name_length_ptr,
domain_ptr, domain_length_ptr, name_use_enum_ptr) == FFI::WIN32_FALSE
raise Puppet::Util::Windows::Error.new(_('Failed to call LookupAccountSidW with bytes: %{sid_bytes}') % { sid_bytes: sid_bytes} )
end
return new(
name_ptr.read_wide_string(name_length_ptr.read_dword),
sid_bytes,
Puppet::Util::Windows::SID.sid_ptr_to_string(sid_ptr),
domain_ptr.read_wide_string(domain_length_ptr.read_dword),
SID_NAME_USE[name_use_enum_ptr.read_uint32])
end
end
end
end
end
end
ensure
system_name_ptr.free if system_name_ptr != FFI::Pointer::NULL
end
end
# Sanitize the given account name for lookup to avoid known issues
def self.sanitize_account_name(account_name)
return account_name unless account_name.start_with?('APPLICATION PACKAGE AUTHORITY\\')
account_name.split('\\').last
end
private_class_method :sanitize_account_name
ffi_convention :stdcall
# https://msdn.microsoft.com/en-us/library/windows/desktop/aa379601(v=vs.85).aspx
SID_NAME_USE = enum(
:SidTypeUser, 1,
:SidTypeGroup, 2,
:SidTypeDomain, 3,
:SidTypeAlias, 4,
:SidTypeWellKnownGroup, 5,
:SidTypeDeletedAccount, 6,
:SidTypeInvalid, 7,
:SidTypeUnknown, 8,
:SidTypeComputer, 9,
:SidTypeLabel, 10
)
# https://msdn.microsoft.com/en-us/library/windows/desktop/aa379159(v=vs.85).aspx
# BOOL WINAPI LookupAccountName(
# _In_opt_ LPCTSTR lpSystemName,
# _In_ LPCTSTR lpAccountName,
# _Out_opt_ PSID Sid,
# _Inout_ LPDWORD cbSid,
# _Out_opt_ LPTSTR ReferencedDomainName,
# _Inout_ LPDWORD cchReferencedDomainName,
# _Out_ PSID_NAME_USE peUse
# );
ffi_lib :advapi32
attach_function_private :LookupAccountNameW,
[:lpcwstr, :lpcwstr, :pointer, :lpdword, :lpwstr, :lpdword, :pointer], :win32_bool
# https://msdn.microsoft.com/en-us/library/windows/desktop/aa379166(v=vs.85).aspx
# BOOL WINAPI LookupAccountSid(
# _In_opt_ LPCTSTR lpSystemName,
# _In_ PSID lpSid,
# _Out_opt_ LPTSTR lpName,
# _Inout_ LPDWORD cchName,
# _Out_opt_ LPTSTR lpReferencedDomainName,
# _Inout_ LPDWORD cchReferencedDomainName,
# _Out_ PSID_NAME_USE peUse
# );
ffi_lib :advapi32
attach_function_private :LookupAccountSidW,
[:lpcwstr, :pointer, :lpwstr, :lpdword, :lpwstr, :lpdword, :pointer], :win32_bool
end
end
|