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
113 def ads_to_principal(ads_object)
114   if !ads_object || !ads_object.respond_to?(:ole_respond_to?) ||
115     !ads_object.ole_respond_to?(:objectSID) || !ads_object.ole_respond_to?(:Name)
116     raise Puppet::Error.new("ads_object must be an IAdsUser or IAdsGroup instance")
117   end
118   octet_string_to_principal(ads_object.objectSID)
119 rescue Puppet::Util::Windows::Error => e
120   # if the error is not a lookup / mapping problem, immediately re-raise
121   raise if e.code != ERROR_NONE_MAPPED
122 
123   # if the Name property isn't formatted like a SID, OR
124   if !valid_sid?(ads_object.Name) ||
125     # if the objectSID doesn't match the Name property, also raise
126     ((converted = octet_string_to_sid_string(ads_object.objectSID)) != ads_object.Name)
127     raise Puppet::Error.new("ads_object Name: #{ads_object.Name} invalid or does not match objectSID: #{ads_object.objectSID} (#{converted})", e)
128   end
129 
130   unresolved_principal(ads_object.Name, ads_object.objectSID)
131 end
get_length_sid(sid_ptr) click to toggle source
    # File lib/puppet/util/windows/sid.rb
219 def get_length_sid(sid_ptr)
220   # MSDN states IsValidSid should be called on pointer first
221   if ! sid_ptr.kind_of?(FFI::Pointer) || IsValidSid(sid_ptr) == FFI::WIN32_FALSE
222     raise Puppet::Util::Windows::Error.new(_("Invalid SID"))
223   end
224 
225   GetLengthSid(sid_ptr)
226 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
68 def name_to_principal(name, allow_unresolved = false)
69   # Apparently, we accept a symbol..
70   name = name.to_s.strip if name
71 
72   # if name is a SID string, convert it to raw bytes for use with lookup_account_sid
73   raw_sid_bytes = nil
74   begin
75     string_to_sid_ptr(name) do |sid_ptr|
76       raw_sid_bytes = sid_ptr.read_array_of_uchar(get_length_sid(sid_ptr))
77     end
78   rescue => e
79     # Avoid debug logs pollution with valid account names
80     # https://docs.microsoft.com/en-us/windows/win32/api/sddl/nf-sddl-convertstringsidtosidw#return-value
81     Puppet.debug("Could not retrieve raw SID bytes from '#{name}': #{e.message}") unless e.code == ERROR_INVALID_SID_STRUCTURE
82   end
83 
84   raw_sid_bytes ? Principal.lookup_account_sid(raw_sid_bytes) : Principal.lookup_account_name(name)
85 rescue => e
86   Puppet.debug("#{e.message}")
87   (allow_unresolved && raw_sid_bytes) ? unresolved_principal(name, raw_sid_bytes) : nil
88 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
56 def name_to_sid(name)
57   sid = name_to_principal(name)
58 
59   sid ? sid.sid : nil
60 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
 97 def octet_string_to_principal(bytes)
 98   if !bytes || !bytes.respond_to?('pack') || bytes.empty?
 99     raise Puppet::Util::Windows::Error.new(_("Octet string must be an array of bytes"))
100   end
101 
102   Principal.lookup_account_sid(bytes)
103 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
229 def octet_string_to_sid_string(sid_bytes)
230   sid_string = nil
231 
232   FFI::MemoryPointer.new(:byte, sid_bytes.length) do |sid_ptr|
233     sid_ptr.write_array_of_uchar(sid_bytes)
234     sid_string = Puppet::Util::Windows::SID.sid_ptr_to_string(sid_ptr)
235   end
236 
237   sid_string
238 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
158 def sid_ptr_to_string(psid)
159   if ! psid.kind_of?(FFI::Pointer) || IsValidSid(psid) == FFI::WIN32_FALSE
160     raise Puppet::Util::Windows::Error.new(_("Invalid SID"))
161   end
162 
163   sid_string = nil
164   FFI::MemoryPointer.new(:pointer, 1) do |buffer_ptr|
165     if ConvertSidToStringSidW(psid, buffer_ptr) == FFI::WIN32_FALSE
166       raise Puppet::Util::Windows::Error.new(_("Failed to convert binary SID"))
167     end
168 
169     buffer_ptr.read_win32_local_pointer do |wide_string_ptr|
170       if wide_string_ptr.null?
171         raise Puppet::Error.new(_("ConvertSidToStringSidW failed to allocate buffer for sid"))
172       end
173 
174       sid_string = wide_string_ptr.read_arbitrary_wide_string_up_to(MAXIMUM_SID_STRING_LENGTH)
175     end
176   end
177 
178   sid_string
179 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
137 def sid_to_name(value)
138 
139   sid_bytes = []
140   begin
141     string_to_sid_ptr(value) do |ptr|
142       sid_bytes = ptr.read_array_of_uchar(get_length_sid(ptr))
143     end
144   rescue Puppet::Util::Windows::Error => e
145     raise if e.code != ERROR_INVALID_SID_STRUCTURE
146   end
147 
148   Principal.lookup_account_sid(sid_bytes).domain_account
149 rescue
150   nil
151 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
186 def string_to_sid_ptr(string_sid, &block)
187   FFI::MemoryPointer.from_string_to_wide_string(string_sid) do |lpcwstr|
188     FFI::MemoryPointer.new(:pointer, 1) do |sid_ptr_ptr|
189 
190       if ConvertStringSidToSidW(lpcwstr, sid_ptr_ptr) == FFI::WIN32_FALSE
191         raise Puppet::Util::Windows::Error.new(_("Failed to convert string SID: %{string_sid}") % { string_sid: string_sid })
192       end
193 
194       sid_ptr_ptr.read_win32_local_pointer do |sid_ptr|
195         yield sid_ptr
196       end
197     end
198   end
199 
200   # yielded sid_ptr has already had LocalFree called, nothing to return
201   nil
202 end
unresolved_principal(name, sid_bytes) click to toggle source

@api private

    # File lib/puppet/util/windows/sid.rb
242 def self.unresolved_principal(name, sid_bytes)
243   Principal.new(
244     name, # account
245     sid_bytes, # sid_bytes
246     name, # sid string
247     nil, #domain
248     # https://msdn.microsoft.com/en-us/library/cc245534.aspx?f=255&MSPPError=-2147217396
249     # Indicates that the type of object could not be determined. For example, no object with that SID exists.
250     :SidTypeUnknown)
251 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
206 def valid_sid?(string_sid)
207   valid = false
208 
209   begin
210     string_to_sid_ptr(string_sid) { |ptr| valid = ! ptr.nil? && ! ptr.null? }
211   rescue Puppet::Util::Windows::Error => e
212     raise if e.code != ERROR_INVALID_SID_STRUCTURE
213   end
214 
215   valid
216 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
113 def ads_to_principal(ads_object)
114   if !ads_object || !ads_object.respond_to?(:ole_respond_to?) ||
115     !ads_object.ole_respond_to?(:objectSID) || !ads_object.ole_respond_to?(:Name)
116     raise Puppet::Error.new("ads_object must be an IAdsUser or IAdsGroup instance")
117   end
118   octet_string_to_principal(ads_object.objectSID)
119 rescue Puppet::Util::Windows::Error => e
120   # if the error is not a lookup / mapping problem, immediately re-raise
121   raise if e.code != ERROR_NONE_MAPPED
122 
123   # if the Name property isn't formatted like a SID, OR
124   if !valid_sid?(ads_object.Name) ||
125     # if the objectSID doesn't match the Name property, also raise
126     ((converted = octet_string_to_sid_string(ads_object.objectSID)) != ads_object.Name)
127     raise Puppet::Error.new("ads_object Name: #{ads_object.Name} invalid or does not match objectSID: #{ads_object.objectSID} (#{converted})", e)
128   end
129 
130   unresolved_principal(ads_object.Name, ads_object.objectSID)
131 end
get_length_sid(sid_ptr) click to toggle source
    # File lib/puppet/util/windows/sid.rb
219 def get_length_sid(sid_ptr)
220   # MSDN states IsValidSid should be called on pointer first
221   if ! sid_ptr.kind_of?(FFI::Pointer) || IsValidSid(sid_ptr) == FFI::WIN32_FALSE
222     raise Puppet::Util::Windows::Error.new(_("Invalid SID"))
223   end
224 
225   GetLengthSid(sid_ptr)
226 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
68 def name_to_principal(name, allow_unresolved = false)
69   # Apparently, we accept a symbol..
70   name = name.to_s.strip if name
71 
72   # if name is a SID string, convert it to raw bytes for use with lookup_account_sid
73   raw_sid_bytes = nil
74   begin
75     string_to_sid_ptr(name) do |sid_ptr|
76       raw_sid_bytes = sid_ptr.read_array_of_uchar(get_length_sid(sid_ptr))
77     end
78   rescue => e
79     # Avoid debug logs pollution with valid account names
80     # https://docs.microsoft.com/en-us/windows/win32/api/sddl/nf-sddl-convertstringsidtosidw#return-value
81     Puppet.debug("Could not retrieve raw SID bytes from '#{name}': #{e.message}") unless e.code == ERROR_INVALID_SID_STRUCTURE
82   end
83 
84   raw_sid_bytes ? Principal.lookup_account_sid(raw_sid_bytes) : Principal.lookup_account_name(name)
85 rescue => e
86   Puppet.debug("#{e.message}")
87   (allow_unresolved && raw_sid_bytes) ? unresolved_principal(name, raw_sid_bytes) : nil
88 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
56 def name_to_sid(name)
57   sid = name_to_principal(name)
58 
59   sid ? sid.sid : nil
60 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
 97 def octet_string_to_principal(bytes)
 98   if !bytes || !bytes.respond_to?('pack') || bytes.empty?
 99     raise Puppet::Util::Windows::Error.new(_("Octet string must be an array of bytes"))
100   end
101 
102   Principal.lookup_account_sid(bytes)
103 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
229 def octet_string_to_sid_string(sid_bytes)
230   sid_string = nil
231 
232   FFI::MemoryPointer.new(:byte, sid_bytes.length) do |sid_ptr|
233     sid_ptr.write_array_of_uchar(sid_bytes)
234     sid_string = Puppet::Util::Windows::SID.sid_ptr_to_string(sid_ptr)
235   end
236 
237   sid_string
238 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
158 def sid_ptr_to_string(psid)
159   if ! psid.kind_of?(FFI::Pointer) || IsValidSid(psid) == FFI::WIN32_FALSE
160     raise Puppet::Util::Windows::Error.new(_("Invalid SID"))
161   end
162 
163   sid_string = nil
164   FFI::MemoryPointer.new(:pointer, 1) do |buffer_ptr|
165     if ConvertSidToStringSidW(psid, buffer_ptr) == FFI::WIN32_FALSE
166       raise Puppet::Util::Windows::Error.new(_("Failed to convert binary SID"))
167     end
168 
169     buffer_ptr.read_win32_local_pointer do |wide_string_ptr|
170       if wide_string_ptr.null?
171         raise Puppet::Error.new(_("ConvertSidToStringSidW failed to allocate buffer for sid"))
172       end
173 
174       sid_string = wide_string_ptr.read_arbitrary_wide_string_up_to(MAXIMUM_SID_STRING_LENGTH)
175     end
176   end
177 
178   sid_string
179 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
137 def sid_to_name(value)
138 
139   sid_bytes = []
140   begin
141     string_to_sid_ptr(value) do |ptr|
142       sid_bytes = ptr.read_array_of_uchar(get_length_sid(ptr))
143     end
144   rescue Puppet::Util::Windows::Error => e
145     raise if e.code != ERROR_INVALID_SID_STRUCTURE
146   end
147 
148   Principal.lookup_account_sid(sid_bytes).domain_account
149 rescue
150   nil
151 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
186 def string_to_sid_ptr(string_sid, &block)
187   FFI::MemoryPointer.from_string_to_wide_string(string_sid) do |lpcwstr|
188     FFI::MemoryPointer.new(:pointer, 1) do |sid_ptr_ptr|
189 
190       if ConvertStringSidToSidW(lpcwstr, sid_ptr_ptr) == FFI::WIN32_FALSE
191         raise Puppet::Util::Windows::Error.new(_("Failed to convert string SID: %{string_sid}") % { string_sid: string_sid })
192       end
193 
194       sid_ptr_ptr.read_win32_local_pointer do |sid_ptr|
195         yield sid_ptr
196       end
197     end
198   end
199 
200   # yielded sid_ptr has already had LocalFree called, nothing to return
201   nil
202 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
206 def valid_sid?(string_sid)
207   valid = false
208 
209   begin
210     string_to_sid_ptr(string_sid) { |ptr| valid = ! ptr.nil? && ! ptr.null? }
211   rescue Puppet::Util::Windows::Error => e
212     raise if e.code != ERROR_INVALID_SID_STRUCTURE
213   end
214 
215   valid
216 end