lib/win32/security/sid.rb in win32-security-0.1.4 vs lib/win32/security/sid.rb in win32-security-0.2.0

- old
+ new

@@ -1,38 +1,25 @@ -require 'windows/security' -require 'windows/thread' -require 'windows/process' -require 'windows/error' -require 'windows/msvcrt/string' -require 'windows/msvcrt/buffer' +require File.join(File.dirname(__FILE__), 'windows', 'constants') +require File.join(File.dirname(__FILE__), 'windows', 'functions') +require File.join(File.dirname(__FILE__), 'windows', 'structs') require 'socket' # The Win32 module serves as a namespace only. module Win32 # The Security class serves as a toplevel class namespace. class Security # The SID class encapsulates a Security Identifier. class SID - include Windows::Security - include Windows::Error - include Windows::MSVCRT::String - include Windows::MSVCRT::Buffer - include Windows::Thread - include Windows::Process + include Windows::Security::Constants + include Windows::Security::Functions + include Windows::Security::Structs + extend Windows::Security::Functions - extend Windows::Security - extend Windows::Error - extend Windows::MSVCRT::String - extend Windows::MSVCRT::Buffer - - # Error class typically raised if any of the SID methods fail - class Error < StandardError; end - # The version of the Win32::Security::SID class. - VERSION = '0.1.4' + VERSION = '0.2.0' # Some constant SID's for your convenience, in string format. # See http://support.microsoft.com/kb/243330 for details. Null = 'S-1-0' @@ -89,44 +76,29 @@ attr_reader :host # Converts a binary SID to a string in S-R-I-S-S... format. # def self.sid_to_string(sid) - sid_addr = [sid].pack('p*').unpack('L')[0] - sid_buf = 0.chr * 80 - sid_ptr = 0.chr * 4 + string_sid = FFI::MemoryPointer.new(:pointer) - unless ConvertSidToStringSid(sid_addr, sid_ptr) - raise Error, get_last_error + unless ConvertSidToStringSid(sid, string_sid) + raise SystemCallError.new("ConvertSidToStringSid", FFI.errno) end - strcpy(sid_buf, sid_ptr.unpack('L')[0]) - sid_buf.strip + string_sid.read_pointer.read_string end # Converts a string in S-R-I-S-S... format back to a binary SID. # def self.string_to_sid(string) - string_addr = [string].pack('p*').unpack('L')[0] - sid_ptr = 0.chr * 4 + sid = FFI::MemoryPointer.new(:pointer) - unless ConvertStringSidToSid(string_addr, sid_ptr) - raise Error, get_last_error + unless ConvertStringSidToSid(string, sid) + raise SystemCallError.new("ConvertStringSidToSid", FFI.errno) end - unless IsValidSid(sid_ptr.unpack('L')[0]) - raise Error, get_last_error - end - - sid_len = GetLengthSid(sid_ptr.unpack('L')[0]) - sid_buf = 0.chr * sid_len - - unless CopySid(sid_len, [sid_buf].pack('p*').unpack('L')[0], sid_ptr.unpack('L')[0]) - raise Error, get_last_error - end - - sid_buf + sid.read_pointer.read_string end # Creates a new SID with +authority+ and up to 8 +subauthorities+, # and returns new Win32::Security::SID object. # @@ -147,28 +119,29 @@ # @domain="" # > # def self.create(authority, *sub_authorities) if sub_authorities.length > 8 - raise ArgumentError, "maximum of 8 subauthorities allowed" + raise ArgumentError, "maximum of 8 subauthorities allowed" end - sid = 0.chr * GetSidLengthRequired(sub_authorities.length) + size = GetSidLengthRequired(sub_authorities.length) + sid = FFI::MemoryPointer.new(:uchar, size) - auth = 0.chr * 5 + authority.chr + auth = SID_IDENTIFIER_AUTHORITY.new + auth[:Value][5] = authority unless InitializeSid(sid, auth, sub_authorities.length) - raise Error, get_last_error + raise SystemCallError.new("InitializeSid", FFI.errno) end sub_authorities.each_index do |i| - value = [sub_authorities[i]].pack('L') - auth_ptr = GetSidSubAuthority(sid, i) - memcpy(auth_ptr, value, 4) + ptr = GetSidSubAuthority(sid, i) + ptr.write_ulong(sub_authorities[i]) end - new(sid) + new(sid.read_string(size)) # Pass a binary string end # Creates and returns a new Win32::Security::SID object, based on # the account name, which may also be a binary SID. If a host is # provided, then the information is retrieved from that host. @@ -196,112 +169,121 @@ # # Binary SID # Win32::Security::SID.new("\001\000\000\000\000\000\001\000\000\000\000") # def initialize(account=nil, host=Socket.gethostname) if account.nil? - htoken = [0].pack('L') - bool = OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, 1, htoken) - errno = GetLastError() + begin + ptoken = FFI::MemoryPointer.new(:ulong) - if !bool - if errno == ERROR_NO_TOKEN - unless OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, htoken) - raise get_last_error - end + # Try the thread token first, default to the process token. + bool = OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, true, ptoken) + + if !bool && FFI.errno != ERROR_NO_TOKEN + raise SystemCallError.new("OpenThreadToken", FFI.errno) else - raise get_last_error(errno) + ptoken = FFI::MemoryPointer.new(:ulong) + unless OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, ptoken) + raise SystemCallError.new("OpenProcessToken", FFI.errno) + end end - end - htoken = htoken.unpack('V').first - cbti = [0].pack('L') - token_info = 0.chr * 36 + token = ptoken.read_ulong + pinfo = FFI::MemoryPointer.new(:pointer) + plength = FFI::MemoryPointer.new(:ulong) - bool = GetTokenInformation( - htoken, - TokenOwner, - token_info, - token_info.size, - cbti - ) + # First pass, just get the size needed (1 is TokenOwner) + GetTokenInformation(token, 1, pinfo, pinfo.size, plength) - unless bool - raise Error, get_last_error + pinfo = FFI::MemoryPointer.new(plength.read_ulong) + plength = FFI::MemoryPointer.new(:ulong) + + # Second pass, actual call (1 is TokenOwner) + unless GetTokenInformation(token, 1, pinfo, pinfo.size, plength) + raise SystemCallError.new("GetTokenInformation", FFI.errno) + end + + token_info = pinfo.read_pointer + ensure + CloseHandle(token) if token end end - bool = false - sid = 0.chr * 28 - sid_cb = [sid.size].pack('L') - - domain_buf = 0.chr * 80 - domain_cch = [domain_buf.size].pack('L') - - sid_name_use = 0.chr * 4 - if account ordinal_val = account[0] ordinal_val = ordinal_val.ord if RUBY_VERSION.to_f >= 1.9 else ordinal_val = nil end + sid = FFI::MemoryPointer.new(:uchar, 260) + sid_size = FFI::MemoryPointer.new(:ulong) + sid_size.write_ulong(sid.size) + + domain = FFI::MemoryPointer.new(:uchar, 260) + domain_size = FFI::MemoryPointer.new(:ulong) + domain_size.write_ulong(domain.size) + + use_ptr = FFI::MemoryPointer.new(:ulong) + if ordinal_val.nil? bool = LookupAccountSid( nil, - token_info.unpack('L')[0], + token_info, sid, - sid_cb, - domain_buf, - domain_cch, - sid_name_use + sid_size, + domain, + domain_size, + use_ptr ) + unless bool + raise SystemCallError.new("LookupAccountSid", FFI.errno) + end elsif ordinal_val < 10 # Assume it's a binary SID. + account_ptr = FFI::MemoryPointer.from_string(account) bool = LookupAccountSid( host, - [account].pack('p*').unpack('L')[0], + account_ptr, sid, - sid_cb, - domain_buf, - domain_cch, - sid_name_use + sid_size, + domain, + domain_size, + use_ptr ) + unless bool + raise SystemCallError.new("LookupAccountSid", FFI.errno) + end else bool = LookupAccountName( host, account, sid, - sid_cb, - domain_buf, - domain_cch, - sid_name_use + sid_size, + domain, + domain_size, + use_ptr, ) + unless bool + raise SystemCallError.new("LookupAccountName", FFI.errno) + end end - unless bool - raise Error, get_last_error - end - # The arguments are flipped depending on which path we took if ordinal_val.nil? - buf = 0.chr * 260 - ptr = token_info.unpack('L')[0] - memcpy(buf, ptr, token_info.size) - @sid = buf.strip - @account = sid.strip + @sid = token_info.read_string + @account = sid.read_string(sid.size).strip elsif ordinal_val < 10 @sid = account - @account = sid.strip + @account = sid.read_string(sid.size).strip else - @sid = sid.strip + @sid = sid.read_string(sid.size).strip @account = account end @host = host - @domain = domain_buf.strip + @domain = domain.read_string - @account_type = get_account_type(sid_name_use.unpack('L')[0]) + @account_type = get_account_type(use_ptr.read_ulong) end # Synonym for SID.new. # def self.open(account=nil, host=Socket.gethostname) @@ -310,19 +292,16 @@ # Returns the binary SID in string format suitable for display, # storage or transmission. # def to_s - sid_addr = [@sid].pack('p*').unpack('L').first - sid_buf = 0.chr * 80 - sid_ptr = 0.chr * 4 + ptr = FFI::MemoryPointer.new(:pointer) - unless ConvertSidToStringSid(sid_addr, sid_ptr) - raise Error, get_last_error + unless ConvertSidToStringSid(@sid, ptr) + raise SystemCallError.new("ConvertSidToStringSid", FFI.errno) end - strcpy(sid_buf, sid_ptr.unpack('L').first) - sid_buf.strip + ptr.read_pointer.read_string end alias to_str to_s # Returns whether or not the SID object is equal to +other+.