lib/win32/security/sid.rb in win32-security-0.1.3 vs lib/win32/security/sid.rb in win32-security-0.1.4
- old
+ new
@@ -1,381 +1,388 @@
-require 'windows/security'
-require 'windows/thread'
-require 'windows/process'
-require 'windows/error'
-require 'windows/msvcrt/string'
-require 'windows/msvcrt/buffer'
-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
-
- 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.3'
-
- # Some constant SID's for your convenience, in string format.
- # See http://support.microsoft.com/kb/243330 for details.
-
- Null = 'S-1-0'
- Nobody = 'S-1-0-0'
- World = 'S-1-1'
- Everyone = 'S-1-1-0'
- Local = 'S-1-2'
- Creator = 'S-1-3'
- CreatorOwner = 'S-1-3-0'
- CreatorGroup = 'S-1-3-1'
- CreatorOwnerServer = 'S-1-3-2'
- CreatorGroupServer = 'S-1-3-3'
- NonUnique = 'S-1-4'
- Nt = 'S-1-5'
- Dialup = 'S-1-5-1'
- Network = 'S-1-5-2'
- Batch = 'S-1-5-3'
- Interactive = 'S-1-5-4'
- Service = 'S-1-5-6'
- Anonymous = 'S-1-5-7'
- Proxy = 'S-1-5-8'
- EnterpriseDomainControllers = 'S-1-5-9'
- PrincipalSelf = 'S-1-5-10'
- AuthenticatedUsers = 'S-1-5-11'
- RestrictedCode = 'S-1-5-12'
- TerminalServerUsers = 'S-1-5-13'
- LocalSystem = 'S-1-5-18'
- NtLocal = 'S-1-5-19'
- NtNetwork = 'S-1-5-20'
- BuiltinAdministrators = 'S-1-5-32-544'
- BuiltinUsers = 'S-1-5-32-545'
- Guests = 'S-1-5-32-546'
- PowerUsers = 'S-1-5-32-547'
- AccountOperators = 'S-1-5-32-548'
- ServerOperators = 'S-1-5-32-549'
- PrintOperators = 'S-1-5-32-550'
- BackupOperators = 'S-1-5-32-551'
- Replicators = 'S-1-5-32-552'
-
- # The binary SID object itself.
- attr_reader :sid
-
- # The account name passed to the constructor.
- attr_reader :account
-
- # The SID account type, e.g. 'user, 'group', etc.
- attr_reader :account_type
-
- # The domain the SID is on.
- attr_reader :domain
-
- # The host passed to the constructor, or the localhost if none
- # was specified.
- 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
-
- unless ConvertSidToStringSid(sid_addr, sid_ptr)
- raise Error, get_last_error
- end
-
- strcpy(sid_buf, sid_ptr.unpack('L')[0])
- sid_buf.strip
- end
-
- # Converts a string in S-R-I-S-S... format back to a binary SID.
- #
- def self.string_to_sid(string)
- sid_buf = 0.chr * 80
- string_addr = [string].pack('p*').unpack('L')[0]
-
- unless ConvertStringSidToSid(string_addr, sid_buf)
- raise Error, get_last_error
- end
-
- if RUBY_VERSION.to_f < 1.9
- sid_buf.strip
- else
- sid_buf.force_encoding('ASCII-8BIT').strip
- end
- end
-
- # Creates a new SID with +authority+ and up to 8 +subauthorities+,
- # and returns new Win32::Security::SID object.
- #
- # Example:
- #
- # sec = Security::SID.create(
- # Security::SID::SECURITY_WORLD_SID_AUTHORITY,
- # Security::SID::SECURITY_WORLD_RID
- # )
- #
- # p sec
- #
- # #<Win32::Security::SID:0x2c5a95c
- # @host="your_host",
- # @account="Everyone",
- # @account_type="well known group",
- # @sid="\001\001\000\000\000\000\000\001\000\000\000\000",
- # @domain=""
- # >
- #
- def self.create(authority, *sub_authorities)
- if sub_authorities.length > 8
- raise ArgumentError, "maximum of 8 subauthorities allowed"
- end
-
- sid = 0.chr * GetSidLengthRequired(sub_authorities.length)
-
- auth = 0.chr * 5 + authority.chr
-
- unless InitializeSid(sid, auth, sub_authorities.length)
- raise Error, get_last_error
- end
-
- sub_authorities.each_index do |i|
- value = [sub_authorities[i]].pack('L')
- auth_ptr = GetSidSubAuthority(sid, i)
- memcpy(auth_ptr, value, 4)
- end
-
- new(sid)
- 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.
- # Otherwise, the local host is used.
- #
- # If no account is provided then it retrieves information for the
- # user account associated with the calling thread and the host argument
- # is ignored.
- #
- # Note that this does NOT create a new SID, but merely retrieves
- # information for an existing SID. To create a new SID, use the
- # SID.create method.
- #
- # Examples:
- #
- # # Current user
- # Win32::Security::SID.new
- #
- # # User 'john' on the localhost
- # Win32::Security::SID.new('john')
- #
- # # User 'jane' on a remote machine
- # Win32::Security::SID.new('jane', 'some_host')
- #
- # # 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()
-
- if !bool
- if errno == ERROR_NO_TOKEN
- unless OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, htoken)
- raise get_last_error
- end
- else
- raise get_last_error(errno)
- end
- end
-
- htoken = htoken.unpack('V').first
- cbti = [0].pack('L')
- token_info = 0.chr * 36
-
- bool = GetTokenInformation(
- htoken,
- TokenOwner,
- token_info,
- token_info.size,
- cbti
- )
-
- unless bool
- raise Error, get_last_error
- 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
-
- if ordinal_val.nil?
- bool = LookupAccountSid(
- nil,
- token_info.unpack('L')[0],
- sid,
- sid_cb,
- domain_buf,
- domain_cch,
- sid_name_use
- )
- elsif ordinal_val < 10 # Assume it's a binary SID.
- bool = LookupAccountSid(
- host,
- [account].pack('p*').unpack('L')[0],
- sid,
- sid_cb,
- domain_buf,
- domain_cch,
- sid_name_use
- )
- else
- bool = LookupAccountName(
- host,
- account,
- sid,
- sid_cb,
- domain_buf,
- domain_cch,
- sid_name_use
- )
- 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
- elsif ordinal_val < 10
- @sid = account
- @account = sid.strip
- else
- @sid = sid.strip
- @account = account
- end
-
- @host = host
- @domain = domain_buf.strip
-
- @account_type = get_account_type(sid_name_use.unpack('L')[0])
- end
-
- # Synonym for SID.new.
- #
- def self.open(account=nil, host=Socket.gethostname)
- new(account, host)
- end
-
- # 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
-
- unless ConvertSidToStringSid(sid_addr, sid_ptr)
- raise Error, get_last_error
- end
-
- strcpy(sid_buf, sid_ptr.unpack('L').first)
- sid_buf.strip
- end
-
- alias to_str to_s
-
- # Returns whether or not the SID object is equal to +other+.
- #
- def ==(other)
- EqualSid(@sid, other.sid)
- end
-
- # Returns whether or not the SID is a valid sid.
- #
- def valid?
- IsValidSid(@sid)
- end
-
- # Returns whether or not the SID is a well known SID.
- #
- # Requires Windows XP or later. Earlier versions will raise a
- # NoMethodError.
- #
- def well_known?
- if defined? IsWellKnownSid
- IsWellKnownSid(@sid)
- else
- raise NoMethodError, 'requires Windows XP or later'
- end
- end
-
- # Returns the length of the SID object, in bytes.
- #
- def length
- GetLengthSid(@sid)
- end
-
- private
-
- # Converts a numeric account type into a human readable string.
- #
- def get_account_type(value)
- case value
- when SidTypeUser
- 'user'
- when SidTypeGroup
- 'group'
- when SidTypeDomain
- 'domain'
- when SidTypeAlias
- 'alias'
- when SidTypeWellKnownGroup
- 'well known group'
- when SidTypeDeletedAccount
- 'deleted account'
- when SidTypeInvalid
- 'invalid'
- when SidTypeUnknown
- 'unknown'
- when SidComputer
- 'computer'
- end
- end
- end
- end
-end
+require 'windows/security'
+require 'windows/thread'
+require 'windows/process'
+require 'windows/error'
+require 'windows/msvcrt/string'
+require 'windows/msvcrt/buffer'
+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
+
+ 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'
+
+ # Some constant SID's for your convenience, in string format.
+ # See http://support.microsoft.com/kb/243330 for details.
+
+ Null = 'S-1-0'
+ Nobody = 'S-1-0-0'
+ World = 'S-1-1'
+ Everyone = 'S-1-1-0'
+ Local = 'S-1-2'
+ Creator = 'S-1-3'
+ CreatorOwner = 'S-1-3-0'
+ CreatorGroup = 'S-1-3-1'
+ CreatorOwnerServer = 'S-1-3-2'
+ CreatorGroupServer = 'S-1-3-3'
+ NonUnique = 'S-1-4'
+ Nt = 'S-1-5'
+ Dialup = 'S-1-5-1'
+ Network = 'S-1-5-2'
+ Batch = 'S-1-5-3'
+ Interactive = 'S-1-5-4'
+ Service = 'S-1-5-6'
+ Anonymous = 'S-1-5-7'
+ Proxy = 'S-1-5-8'
+ EnterpriseDomainControllers = 'S-1-5-9'
+ PrincipalSelf = 'S-1-5-10'
+ AuthenticatedUsers = 'S-1-5-11'
+ RestrictedCode = 'S-1-5-12'
+ TerminalServerUsers = 'S-1-5-13'
+ LocalSystem = 'S-1-5-18'
+ NtLocal = 'S-1-5-19'
+ NtNetwork = 'S-1-5-20'
+ BuiltinAdministrators = 'S-1-5-32-544'
+ BuiltinUsers = 'S-1-5-32-545'
+ Guests = 'S-1-5-32-546'
+ PowerUsers = 'S-1-5-32-547'
+ AccountOperators = 'S-1-5-32-548'
+ ServerOperators = 'S-1-5-32-549'
+ PrintOperators = 'S-1-5-32-550'
+ BackupOperators = 'S-1-5-32-551'
+ Replicators = 'S-1-5-32-552'
+
+ # The binary SID object itself.
+ attr_reader :sid
+
+ # The account name passed to the constructor.
+ attr_reader :account
+
+ # The SID account type, e.g. 'user, 'group', etc.
+ attr_reader :account_type
+
+ # The domain the SID is on.
+ attr_reader :domain
+
+ # The host passed to the constructor, or the localhost if none
+ # was specified.
+ 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
+
+ unless ConvertSidToStringSid(sid_addr, sid_ptr)
+ raise Error, get_last_error
+ end
+
+ strcpy(sid_buf, sid_ptr.unpack('L')[0])
+ sid_buf.strip
+ 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
+
+ unless ConvertStringSidToSid(string_addr, sid_ptr)
+ raise Error, get_last_error
+ 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
+ end
+
+ # Creates a new SID with +authority+ and up to 8 +subauthorities+,
+ # and returns new Win32::Security::SID object.
+ #
+ # Example:
+ #
+ # sec = Security::SID.create(
+ # Security::SID::SECURITY_WORLD_SID_AUTHORITY,
+ # Security::SID::SECURITY_WORLD_RID
+ # )
+ #
+ # p sec
+ #
+ # #<Win32::Security::SID:0x2c5a95c
+ # @host="your_host",
+ # @account="Everyone",
+ # @account_type="well known group",
+ # @sid="\001\001\000\000\000\000\000\001\000\000\000\000",
+ # @domain=""
+ # >
+ #
+ def self.create(authority, *sub_authorities)
+ if sub_authorities.length > 8
+ raise ArgumentError, "maximum of 8 subauthorities allowed"
+ end
+
+ sid = 0.chr * GetSidLengthRequired(sub_authorities.length)
+
+ auth = 0.chr * 5 + authority.chr
+
+ unless InitializeSid(sid, auth, sub_authorities.length)
+ raise Error, get_last_error
+ end
+
+ sub_authorities.each_index do |i|
+ value = [sub_authorities[i]].pack('L')
+ auth_ptr = GetSidSubAuthority(sid, i)
+ memcpy(auth_ptr, value, 4)
+ end
+
+ new(sid)
+ 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.
+ # Otherwise, the local host is used.
+ #
+ # If no account is provided then it retrieves information for the
+ # user account associated with the calling thread and the host argument
+ # is ignored.
+ #
+ # Note that this does NOT create a new SID, but merely retrieves
+ # information for an existing SID. To create a new SID, use the
+ # SID.create method.
+ #
+ # Examples:
+ #
+ # # Current user
+ # Win32::Security::SID.new
+ #
+ # # User 'john' on the localhost
+ # Win32::Security::SID.new('john')
+ #
+ # # User 'jane' on a remote machine
+ # Win32::Security::SID.new('jane', 'some_host')
+ #
+ # # 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()
+
+ if !bool
+ if errno == ERROR_NO_TOKEN
+ unless OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, htoken)
+ raise get_last_error
+ end
+ else
+ raise get_last_error(errno)
+ end
+ end
+
+ htoken = htoken.unpack('V').first
+ cbti = [0].pack('L')
+ token_info = 0.chr * 36
+
+ bool = GetTokenInformation(
+ htoken,
+ TokenOwner,
+ token_info,
+ token_info.size,
+ cbti
+ )
+
+ unless bool
+ raise Error, get_last_error
+ 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
+
+ if ordinal_val.nil?
+ bool = LookupAccountSid(
+ nil,
+ token_info.unpack('L')[0],
+ sid,
+ sid_cb,
+ domain_buf,
+ domain_cch,
+ sid_name_use
+ )
+ elsif ordinal_val < 10 # Assume it's a binary SID.
+ bool = LookupAccountSid(
+ host,
+ [account].pack('p*').unpack('L')[0],
+ sid,
+ sid_cb,
+ domain_buf,
+ domain_cch,
+ sid_name_use
+ )
+ else
+ bool = LookupAccountName(
+ host,
+ account,
+ sid,
+ sid_cb,
+ domain_buf,
+ domain_cch,
+ sid_name_use
+ )
+ 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
+ elsif ordinal_val < 10
+ @sid = account
+ @account = sid.strip
+ else
+ @sid = sid.strip
+ @account = account
+ end
+
+ @host = host
+ @domain = domain_buf.strip
+
+ @account_type = get_account_type(sid_name_use.unpack('L')[0])
+ end
+
+ # Synonym for SID.new.
+ #
+ def self.open(account=nil, host=Socket.gethostname)
+ new(account, host)
+ end
+
+ # 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
+
+ unless ConvertSidToStringSid(sid_addr, sid_ptr)
+ raise Error, get_last_error
+ end
+
+ strcpy(sid_buf, sid_ptr.unpack('L').first)
+ sid_buf.strip
+ end
+
+ alias to_str to_s
+
+ # Returns whether or not the SID object is equal to +other+.
+ #
+ def ==(other)
+ EqualSid(@sid, other.sid)
+ end
+
+ # Returns whether or not the SID is a valid sid.
+ #
+ def valid?
+ IsValidSid(@sid)
+ end
+
+ # Returns whether or not the SID is a well known SID.
+ #
+ # Requires Windows XP or later. Earlier versions will raise a
+ # NoMethodError.
+ #
+ def well_known?
+ if defined? IsWellKnownSid
+ IsWellKnownSid(@sid)
+ else
+ raise NoMethodError, 'requires Windows XP or later'
+ end
+ end
+
+ # Returns the length of the SID object, in bytes.
+ #
+ def length
+ GetLengthSid(@sid)
+ end
+
+ private
+
+ # Converts a numeric account type into a human readable string.
+ #
+ def get_account_type(value)
+ case value
+ when SidTypeUser
+ 'user'
+ when SidTypeGroup
+ 'group'
+ when SidTypeDomain
+ 'domain'
+ when SidTypeAlias
+ 'alias'
+ when SidTypeWellKnownGroup
+ 'well known group'
+ when SidTypeDeletedAccount
+ 'deleted account'
+ when SidTypeInvalid
+ 'invalid'
+ when SidTypeUnknown
+ 'unknown'
+ when SidComputer
+ 'computer'
+ end
+ end
+ end
+ end
+end