module Puppet::Util::Windows::SID

Constants

AccountOperators
AllAppPackages
Anonymous
AuthenticatedUsers
BackupOperators
Batch
BuiltinAdministrators
BuiltinUsers
Creator
CreatorGroup
CreatorGroupServer
CreatorOwner
CreatorOwnerServer
Dialup
ERROR_INVALID_SID_STRUCTURE
ERROR_NONE_MAPPED

missing from Windows::Error

EnterpriseDomainControllers
Everyone
Guests
Interactive
Local
LocalSystem
MAXIMUM_SID_STRING_LENGTH

stackoverflow.com/a/1792930 - 68 bytes, 184 characters in a string

Network
Nobody
NonUnique
Nt
NtLocal
NtNetwork
Null

Well Known SIDs

PowerUsers
PrincipalSelf
PrintOperators
Proxy
Replicators
RestrictedCode
ServerOperators
Service
TerminalServerUsers
World

Public Class Methods

ads_to_principal(ads_object) click to toggle source

Converts a COM instance of IAdsUser or IAdsGroup to a SID::Principal object, Raises an Error for nil or an object without an objectSID / Name property. This method returns a SID::Principal with the account, domain, SID, etc This method will return instances even when the SID is unresolvable, as may be the case when domain users have been added to local groups, but removed from the domain

    # File lib/puppet/util/windows/sid.rb
112 def ads_to_principal(ads_object)
113   if !ads_object || !ads_object.respond_to?(:ole_respond_to?) ||
114     !ads_object.ole_respond_to?(:objectSID) || !ads_object.ole_respond_to?(:Name)
115     raise Puppet::Error.new("ads_object must be an IAdsUser or IAdsGroup instance")
116   end
117   octet_string_to_principal(ads_object.objectSID)
118 rescue Puppet::Util::Windows::Error => e
119   # if the error is not a lookup / mapping problem, immediately re-raise
120   raise if e.code != ERROR_NONE_MAPPED
121 
122   # if the Name property isn't formatted like a SID, OR
123   if !valid_sid?(ads_object.Name) ||
124     # if the objectSID doesn't match the Name property, also raise
125     ((converted = octet_string_to_sid_string(ads_object.objectSID)) != ads_object.Name)
126     raise Puppet::Error.new("ads_object Name: #{ads_object.Name} invalid or does not match objectSID: #{ads_object.objectSID} (#{converted})", e)
127   end
128 
129   unresolved_principal(ads_object.Name, ads_object.objectSID)
130 end
get_length_sid(sid_ptr) click to toggle source
    # File lib/puppet/util/windows/sid.rb
218 def get_length_sid(sid_ptr)
219   # MSDN states IsValidSid should be called on pointer first
220   if ! sid_ptr.kind_of?(FFI::Pointer) || IsValidSid(sid_ptr) == FFI::WIN32_FALSE
221     raise Puppet::Util::Windows::Error.new(_("Invalid SID"))
222   end
223 
224   GetLengthSid(sid_ptr)
225 end
name_to_principal(name, allow_unresolved = false) click to toggle source

Convert an account name, e.g. 'Administrators' into a Principal::SID object, e.g. 'S-1-5-32-544'. The name can be specified as 'Administrators', 'BUILTINAdministrators', or 'S-1-5-32-544', and will return the SID object. Returns nil if the account doesn't exist. This method returns a SID::Principal with the account, domain, SID, etc

   # File lib/puppet/util/windows/sid.rb
67 def name_to_principal(name, allow_unresolved = false)
68   # Apparently, we accept a symbol..
69   name = name.to_s.strip if name
70 
71   # if name is a SID string, convert it to raw bytes for use with lookup_account_sid
72   raw_sid_bytes = nil
73   begin
74     string_to_sid_ptr(name) do |sid_ptr|
75       raw_sid_bytes = sid_ptr.read_array_of_uchar(get_length_sid(sid_ptr))
76     end
77   rescue => e
78     # Avoid debug logs pollution with valid account names
79     # https://docs.microsoft.com/en-us/windows/win32/api/sddl/nf-sddl-convertstringsidtosidw#return-value
80     Puppet.debug("Could not retrieve raw SID bytes from '#{name}': #{e.message}") unless e.code == ERROR_INVALID_SID_STRUCTURE
81   end
82 
83   raw_sid_bytes ? Principal.lookup_account_sid(raw_sid_bytes) : Principal.lookup_account_name(name)
84 rescue => e
85   Puppet.debug("#{e.message}")
86   (allow_unresolved && raw_sid_bytes) ? unresolved_principal(name, raw_sid_bytes) : nil
87 end
Also aliased as: name_to_sid_object
name_to_sid(name) click to toggle source

Convert an account name, e.g. 'Administrators' into a SID string, e.g. 'S-1-5-32-544'. The name can be specified as 'Administrators', 'BUILTINAdministrators', or 'S-1-5-32-544', and will return the SID. Returns nil if the account doesn't exist.

   # File lib/puppet/util/windows/sid.rb
55 def name_to_sid(name)
56   sid = name_to_principal(name)
57 
58   sid ? sid.sid : nil
59 end
name_to_sid_object(name, allow_unresolved = false)
Alias for: name_to_principal
octet_string_to_principal(bytes) click to toggle source

Converts an octet string array of bytes to a SID::Principal object, e.g. [1, 1, 0, 0, 0, 0, 0, 5, 18, 0, 0, 0] is the representation for S-1-5-18, the local 'SYSTEM' account. Raises an Error for nil or non-array input. This method returns a SID::Principal with the account, domain, SID, etc

    # File lib/puppet/util/windows/sid.rb
 96 def octet_string_to_principal(bytes)
 97   if !bytes || !bytes.respond_to?('pack') || bytes.empty?
 98     raise Puppet::Util::Windows::Error.new(_("Octet string must be an array of bytes"))
 99   end
100 
101   Principal.lookup_account_sid(bytes)
102 end
Also aliased as: octet_string_to_sid_object
octet_string_to_sid_object(bytes)
octet_string_to_sid_string(sid_bytes) click to toggle source
    # File lib/puppet/util/windows/sid.rb
228 def octet_string_to_sid_string(sid_bytes)
229   sid_string = nil
230 
231   FFI::MemoryPointer.new(:byte, sid_bytes.length) do |sid_ptr|
232     sid_ptr.write_array_of_uchar(sid_bytes)
233     sid_string = Puppet::Util::Windows::SID.sid_ptr_to_string(sid_ptr)
234   end
235 
236   sid_string
237 end
sid_ptr_to_string(psid) click to toggle source

Convert a SID pointer to a SID string, e.g. “S-1-5-32-544”.

    # File lib/puppet/util/windows/sid.rb
157 def sid_ptr_to_string(psid)
158   if ! psid.kind_of?(FFI::Pointer) || IsValidSid(psid) == FFI::WIN32_FALSE
159     raise Puppet::Util::Windows::Error.new(_("Invalid SID"))
160   end
161 
162   sid_string = nil
163   FFI::MemoryPointer.new(:pointer, 1) do |buffer_ptr|
164     if ConvertSidToStringSidW(psid, buffer_ptr) == FFI::WIN32_FALSE
165       raise Puppet::Util::Windows::Error.new(_("Failed to convert binary SID"))
166     end
167 
168     buffer_ptr.read_win32_local_pointer do |wide_string_ptr|
169       if wide_string_ptr.null?
170         raise Puppet::Error.new(_("ConvertSidToStringSidW failed to allocate buffer for sid"))
171       end
172 
173       sid_string = wide_string_ptr.read_arbitrary_wide_string_up_to(MAXIMUM_SID_STRING_LENGTH)
174     end
175   end
176 
177   sid_string
178 end
sid_to_name(value) click to toggle source

Convert a SID string, e.g. “S-1-5-32-544” to a name, e.g. 'BUILTINAdministrators'. Returns nil if an account for that SID does not exist.

    # File lib/puppet/util/windows/sid.rb
136 def sid_to_name(value)
137 
138   sid_bytes = []
139   begin
140     string_to_sid_ptr(value) do |ptr|
141       sid_bytes = ptr.read_array_of_uchar(get_length_sid(ptr))
142     end
143   rescue Puppet::Util::Windows::Error => e
144     raise if e.code != ERROR_INVALID_SID_STRUCTURE
145   end
146 
147   Principal.lookup_account_sid(sid_bytes).domain_account
148 rescue
149   nil
150 end
string_to_sid_ptr(string_sid) { |sid_ptr| ... } click to toggle source

Convert a SID string, e.g. “S-1-5-32-544” to a pointer (containing the address of the binary SID structure). The returned value can be used in Win32 APIs that expect a PSID, e.g. IsValidSid. The account for this SID may or may not exist.

    # File lib/puppet/util/windows/sid.rb
185 def string_to_sid_ptr(string_sid, &block)
186   FFI::MemoryPointer.from_string_to_wide_string(string_sid) do |lpcwstr|
187     FFI::MemoryPointer.new(:pointer, 1) do |sid_ptr_ptr|
188 
189       if ConvertStringSidToSidW(lpcwstr, sid_ptr_ptr) == FFI::WIN32_FALSE
190         raise Puppet::Util::Windows::Error.new(_("Failed to convert string SID: %{string_sid}") % { string_sid: string_sid })
191       end
192 
193       sid_ptr_ptr.read_win32_local_pointer do |sid_ptr|
194         yield sid_ptr
195       end
196     end
197   end
198 
199   # yielded sid_ptr has already had LocalFree called, nothing to return
200   nil
201 end
unresolved_principal(name, sid_bytes) click to toggle source

@api private

    # File lib/puppet/util/windows/sid.rb
241 def self.unresolved_principal(name, sid_bytes)
242   Principal.new(
243     name, # account
244     sid_bytes, # sid_bytes
245     name, # sid string
246     nil, #domain
247     # https://msdn.microsoft.com/en-us/library/cc245534.aspx?f=255&MSPPError=-2147217396
248     # Indicates that the type of object could not be determined. For example, no object with that SID exists.
249     :SidTypeUnknown)
250 end
valid_sid?(string_sid) click to toggle source

Return true if the string is a valid SID, e.g. “S-1-5-32-544”, false otherwise.

    # File lib/puppet/util/windows/sid.rb
205 def valid_sid?(string_sid)
206   valid = false
207 
208   begin
209     string_to_sid_ptr(string_sid) { |ptr| valid = ! ptr.nil? && ! ptr.null? }
210   rescue Puppet::Util::Windows::Error => e
211     raise if e.code != ERROR_INVALID_SID_STRUCTURE
212   end
213 
214   valid
215 end

Private Instance Methods

ads_to_principal(ads_object) click to toggle source

Converts a COM instance of IAdsUser or IAdsGroup to a SID::Principal object, Raises an Error for nil or an object without an objectSID / Name property. This method returns a SID::Principal with the account, domain, SID, etc This method will return instances even when the SID is unresolvable, as may be the case when domain users have been added to local groups, but removed from the domain

    # File lib/puppet/util/windows/sid.rb
112 def ads_to_principal(ads_object)
113   if !ads_object || !ads_object.respond_to?(:ole_respond_to?) ||
114     !ads_object.ole_respond_to?(:objectSID) || !ads_object.ole_respond_to?(:Name)
115     raise Puppet::Error.new("ads_object must be an IAdsUser or IAdsGroup instance")
116   end
117   octet_string_to_principal(ads_object.objectSID)
118 rescue Puppet::Util::Windows::Error => e
119   # if the error is not a lookup / mapping problem, immediately re-raise
120   raise if e.code != ERROR_NONE_MAPPED
121 
122   # if the Name property isn't formatted like a SID, OR
123   if !valid_sid?(ads_object.Name) ||
124     # if the objectSID doesn't match the Name property, also raise
125     ((converted = octet_string_to_sid_string(ads_object.objectSID)) != ads_object.Name)
126     raise Puppet::Error.new("ads_object Name: #{ads_object.Name} invalid or does not match objectSID: #{ads_object.objectSID} (#{converted})", e)
127   end
128 
129   unresolved_principal(ads_object.Name, ads_object.objectSID)
130 end
get_length_sid(sid_ptr) click to toggle source
    # File lib/puppet/util/windows/sid.rb
218 def get_length_sid(sid_ptr)
219   # MSDN states IsValidSid should be called on pointer first
220   if ! sid_ptr.kind_of?(FFI::Pointer) || IsValidSid(sid_ptr) == FFI::WIN32_FALSE
221     raise Puppet::Util::Windows::Error.new(_("Invalid SID"))
222   end
223 
224   GetLengthSid(sid_ptr)
225 end
name_to_principal(name, allow_unresolved = false) click to toggle source

Convert an account name, e.g. 'Administrators' into a Principal::SID object, e.g. 'S-1-5-32-544'. The name can be specified as 'Administrators', 'BUILTINAdministrators', or 'S-1-5-32-544', and will return the SID object. Returns nil if the account doesn't exist. This method returns a SID::Principal with the account, domain, SID, etc

   # File lib/puppet/util/windows/sid.rb
67 def name_to_principal(name, allow_unresolved = false)
68   # Apparently, we accept a symbol..
69   name = name.to_s.strip if name
70 
71   # if name is a SID string, convert it to raw bytes for use with lookup_account_sid
72   raw_sid_bytes = nil
73   begin
74     string_to_sid_ptr(name) do |sid_ptr|
75       raw_sid_bytes = sid_ptr.read_array_of_uchar(get_length_sid(sid_ptr))
76     end
77   rescue => e
78     # Avoid debug logs pollution with valid account names
79     # https://docs.microsoft.com/en-us/windows/win32/api/sddl/nf-sddl-convertstringsidtosidw#return-value
80     Puppet.debug("Could not retrieve raw SID bytes from '#{name}': #{e.message}") unless e.code == ERROR_INVALID_SID_STRUCTURE
81   end
82 
83   raw_sid_bytes ? Principal.lookup_account_sid(raw_sid_bytes) : Principal.lookup_account_name(name)
84 rescue => e
85   Puppet.debug("#{e.message}")
86   (allow_unresolved && raw_sid_bytes) ? unresolved_principal(name, raw_sid_bytes) : nil
87 end
Also aliased as: name_to_sid_object
name_to_sid(name) click to toggle source

Convert an account name, e.g. 'Administrators' into a SID string, e.g. 'S-1-5-32-544'. The name can be specified as 'Administrators', 'BUILTINAdministrators', or 'S-1-5-32-544', and will return the SID. Returns nil if the account doesn't exist.

   # File lib/puppet/util/windows/sid.rb
55 def name_to_sid(name)
56   sid = name_to_principal(name)
57 
58   sid ? sid.sid : nil
59 end
octet_string_to_principal(bytes) click to toggle source

Converts an octet string array of bytes to a SID::Principal object, e.g. [1, 1, 0, 0, 0, 0, 0, 5, 18, 0, 0, 0] is the representation for S-1-5-18, the local 'SYSTEM' account. Raises an Error for nil or non-array input. This method returns a SID::Principal with the account, domain, SID, etc

    # File lib/puppet/util/windows/sid.rb
 96 def octet_string_to_principal(bytes)
 97   if !bytes || !bytes.respond_to?('pack') || bytes.empty?
 98     raise Puppet::Util::Windows::Error.new(_("Octet string must be an array of bytes"))
 99   end
100 
101   Principal.lookup_account_sid(bytes)
102 end
Also aliased as: octet_string_to_sid_object
octet_string_to_sid_string(sid_bytes) click to toggle source
    # File lib/puppet/util/windows/sid.rb
228 def octet_string_to_sid_string(sid_bytes)
229   sid_string = nil
230 
231   FFI::MemoryPointer.new(:byte, sid_bytes.length) do |sid_ptr|
232     sid_ptr.write_array_of_uchar(sid_bytes)
233     sid_string = Puppet::Util::Windows::SID.sid_ptr_to_string(sid_ptr)
234   end
235 
236   sid_string
237 end
sid_ptr_to_string(psid) click to toggle source

Convert a SID pointer to a SID string, e.g. “S-1-5-32-544”.

    # File lib/puppet/util/windows/sid.rb
157 def sid_ptr_to_string(psid)
158   if ! psid.kind_of?(FFI::Pointer) || IsValidSid(psid) == FFI::WIN32_FALSE
159     raise Puppet::Util::Windows::Error.new(_("Invalid SID"))
160   end
161 
162   sid_string = nil
163   FFI::MemoryPointer.new(:pointer, 1) do |buffer_ptr|
164     if ConvertSidToStringSidW(psid, buffer_ptr) == FFI::WIN32_FALSE
165       raise Puppet::Util::Windows::Error.new(_("Failed to convert binary SID"))
166     end
167 
168     buffer_ptr.read_win32_local_pointer do |wide_string_ptr|
169       if wide_string_ptr.null?
170         raise Puppet::Error.new(_("ConvertSidToStringSidW failed to allocate buffer for sid"))
171       end
172 
173       sid_string = wide_string_ptr.read_arbitrary_wide_string_up_to(MAXIMUM_SID_STRING_LENGTH)
174     end
175   end
176 
177   sid_string
178 end
sid_to_name(value) click to toggle source

Convert a SID string, e.g. “S-1-5-32-544” to a name, e.g. 'BUILTINAdministrators'. Returns nil if an account for that SID does not exist.

    # File lib/puppet/util/windows/sid.rb
136 def sid_to_name(value)
137 
138   sid_bytes = []
139   begin
140     string_to_sid_ptr(value) do |ptr|
141       sid_bytes = ptr.read_array_of_uchar(get_length_sid(ptr))
142     end
143   rescue Puppet::Util::Windows::Error => e
144     raise if e.code != ERROR_INVALID_SID_STRUCTURE
145   end
146 
147   Principal.lookup_account_sid(sid_bytes).domain_account
148 rescue
149   nil
150 end
string_to_sid_ptr(string_sid) { |sid_ptr| ... } click to toggle source

Convert a SID string, e.g. “S-1-5-32-544” to a pointer (containing the address of the binary SID structure). The returned value can be used in Win32 APIs that expect a PSID, e.g. IsValidSid. The account for this SID may or may not exist.

    # File lib/puppet/util/windows/sid.rb
185 def string_to_sid_ptr(string_sid, &block)
186   FFI::MemoryPointer.from_string_to_wide_string(string_sid) do |lpcwstr|
187     FFI::MemoryPointer.new(:pointer, 1) do |sid_ptr_ptr|
188 
189       if ConvertStringSidToSidW(lpcwstr, sid_ptr_ptr) == FFI::WIN32_FALSE
190         raise Puppet::Util::Windows::Error.new(_("Failed to convert string SID: %{string_sid}") % { string_sid: string_sid })
191       end
192 
193       sid_ptr_ptr.read_win32_local_pointer do |sid_ptr|
194         yield sid_ptr
195       end
196     end
197   end
198 
199   # yielded sid_ptr has already had LocalFree called, nothing to return
200   nil
201 end
valid_sid?(string_sid) click to toggle source

Return true if the string is a valid SID, e.g. “S-1-5-32-544”, false otherwise.

    # File lib/puppet/util/windows/sid.rb
205 def valid_sid?(string_sid)
206   valid = false
207 
208   begin
209     string_to_sid_ptr(string_sid) { |ptr| valid = ! ptr.nil? && ! ptr.null? }
210   rescue Puppet::Util::Windows::Error => e
211     raise if e.code != ERROR_INVALID_SID_STRUCTURE
212   end
213 
214   valid
215 end