module Puppet::Util::Windows::Security

Constants

ACL_REVISION
DACL_SECURITY_INFORMATION
DELETE
FILE
FILE_PERSISTENT_ACLS
GROUP_SECURITY_INFORMATION
MASK_TO_MODE
MAXIMUM_GENERIC_ACE_SIZE
MAXIMUM_SID_BYTES_LENGTH

stackoverflow.com/a/1792930

MODE_TO_MASK
NO_INHERITANCE
OWNER_SECURITY_INFORMATION
PROTECTED_DACL_SECURITY_INFORMATION

constants that are missing from Windows::Security

READ_CONTROL
SE_BACKUP_NAME
SE_DACL_PROTECTED
SE_DEBUG_NAME
SE_OBJECT_TYPE

msdn.microsoft.com/en-us/library/windows/desktop/aa379593(v=vs.85).aspx

SE_PRIVILEGE_ENABLED
SE_RESTORE_NAME
S_IEXTRA
S_IRGRP
S_IROTH
S_IRUSR

file modes

S_IRWXG
S_IRWXO
S_IRWXU
S_ISVTX
S_ISYSTEM_MISSING
S_IWGRP
S_IWOTH
S_IWUSR
S_IXGRP
S_IXOTH
S_IXUSR
TOKEN_ADJUST_PRIVILEGES
UNPROTECTED_DACL_SECURITY_INFORMATION
WRITE_DAC
WRITE_OWNER

Public Instance Methods

add_access_allowed_ace(acl, mask, sid, inherit = nil) click to toggle source
    # File lib/puppet/util/windows/security.rb
429 def add_access_allowed_ace(acl, mask, sid, inherit = nil)
430   inherit ||= NO_INHERITANCE
431 
432   Puppet::Util::Windows::SID.string_to_sid_ptr(sid) do |sid_ptr|
433     if Puppet::Util::Windows::SID.IsValidSid(sid_ptr) == FFI::WIN32_FALSE
434       raise Puppet::Util::Windows::Error.new(_("Invalid SID"))
435     end
436 
437     if AddAccessAllowedAceEx(acl, ACL_REVISION, inherit, mask, sid_ptr) == FFI::WIN32_FALSE
438       raise Puppet::Util::Windows::Error.new(_("Failed to add access control entry"))
439     end
440   end
441 
442   # ensure this method is void if it doesn't raise
443   nil
444 end
add_access_denied_ace(acl, mask, sid, inherit = nil) click to toggle source
    # File lib/puppet/util/windows/security.rb
446 def add_access_denied_ace(acl, mask, sid, inherit = nil)
447   inherit ||= NO_INHERITANCE
448 
449   Puppet::Util::Windows::SID.string_to_sid_ptr(sid) do |sid_ptr|
450     if Puppet::Util::Windows::SID.IsValidSid(sid_ptr) == FFI::WIN32_FALSE
451       raise Puppet::Util::Windows::Error.new(_("Invalid SID"))
452     end
453 
454     if AddAccessDeniedAceEx(acl, ACL_REVISION, inherit, mask, sid_ptr) == FFI::WIN32_FALSE
455       raise Puppet::Util::Windows::Error.new(_("Failed to add access control entry"))
456     end
457   end
458 
459   # ensure this method is void if it doesn't raise
460   nil
461 end
get_aces_for_path_by_sid(path, sid) click to toggle source
    # File lib/puppet/util/windows/security.rb
189 def get_aces_for_path_by_sid(path, sid)
190   get_security_descriptor(path).dacl.select { |ace| ace.sid == sid }
191 end
get_group(path) click to toggle source

Get the group of the object referenced by path. The returned value is a SID string, e.g. “S-1-5-32-544”. Any user with read access to an object can get the group. Only a user with the SE_BACKUP_NAME privilege in their process token can get the group for objects they do not have read access to.

    # File lib/puppet/util/windows/security.rb
157 def get_group(path)
158   return unless supports_acl?(path)
159 
160   get_security_descriptor(path).group
161 end
get_max_generic_acl_size(ace_count) click to toggle source
    # File lib/puppet/util/windows/security.rb
630 def get_max_generic_acl_size(ace_count)
631   # https://msdn.microsoft.com/en-us/library/windows/desktop/aa378853(v=vs.85).aspx
632   # To calculate the initial size of an ACL, add the following together, and then align the result to the nearest DWORD:
633   # * Size of the ACL structure.
634   # * Size of each ACE structure that the ACL is to contain minus the SidStart member (DWORD) of the ACE.
635   # * Length of the SID that each ACE is to contain.
636   ACL.size + ace_count * MAXIMUM_GENERIC_ACE_SIZE
637 end
get_mode(path) click to toggle source

Get the mode of the object referenced by path. The returned integer value represents the POSIX-style read, write, and execute modes for the user, group, and other classes, e.g. 0640. Any user with read access to an object can get the mode. Only a user with the SE_BACKUP_NAME privilege in their process token can get the mode for objects they do not have read access to.

    # File lib/puppet/util/windows/security.rb
199 def get_mode(path)
200   return unless supports_acl?(path)
201 
202   well_known_world_sid = Puppet::Util::Windows::SID::Everyone
203   well_known_nobody_sid = Puppet::Util::Windows::SID::Nobody
204   well_known_system_sid = Puppet::Util::Windows::SID::LocalSystem
205   well_known_app_packages_sid = Puppet::Util::Windows::SID::AllAppPackages
206 
207   mode = S_ISYSTEM_MISSING
208 
209   sd = get_security_descriptor(path)
210   sd.dacl.each do |ace|
211     next if ace.inherit_only?
212 
213     case ace.sid
214     when sd.owner
215       MASK_TO_MODE.each_pair do |k,v|
216         if (ace.mask & k) == k
217           mode |= (v << 6)
218         end
219       end
220     when sd.group
221       MASK_TO_MODE.each_pair do |k,v|
222         if (ace.mask & k) == k
223           mode |= (v << 3)
224         end
225       end
226     when well_known_world_sid
227       MASK_TO_MODE.each_pair do |k,v|
228         if (ace.mask & k) == k
229           mode |= (v << 6) | (v << 3) | v
230         end
231       end
232       if File.directory?(path) &&
233         (ace.mask & (FILE::FILE_WRITE_DATA | FILE::FILE_EXECUTE | FILE::FILE_DELETE_CHILD)) == (FILE::FILE_WRITE_DATA | FILE::FILE_EXECUTE)
234         mode |= S_ISVTX;
235       end
236     when well_known_nobody_sid
237       if (ace.mask & FILE::FILE_APPEND_DATA).nonzero?
238         mode |= S_ISVTX
239       end
240     when well_known_app_packages_sid
241     when well_known_system_sid
242     else
243       #puts "Warning, unable to map SID into POSIX mode: #{ace.sid}"
244       mode |= S_IEXTRA
245     end
246 
247     if ace.sid == well_known_system_sid
248       mode &= ~S_ISYSTEM_MISSING
249     end
250 
251     # if owner and group the same, then user and group modes are the OR of both
252     if sd.owner == sd.group
253       mode |= ((mode & S_IRWXG) << 3) | ((mode & S_IRWXU) >> 3)
254       #puts "owner: #{sd.group}, 0x#{ace.mask.to_s(16)}, #{mode.to_s(8)}"
255     end
256   end
257 
258   #puts "get_mode: #{mode.to_s(8)}"
259   mode
260 end
get_owner(path) click to toggle source

Get the owner of the object referenced by path. The returned value is a SID string, e.g. “S-1-5-32-544”. Any user with read access to an object can get the owner. Only a user with the SE_BACKUP_NAME privilege in their process token can get the owner for objects they do not have read access to.

    # File lib/puppet/util/windows/security.rb
132 def get_owner(path)
133   return unless supports_acl?(path)
134 
135   get_security_descriptor(path).owner
136 end
get_security_descriptor(path) click to toggle source
    # File lib/puppet/util/windows/security.rb
581 def get_security_descriptor(path)
582   sd = nil
583 
584   with_privilege(SE_BACKUP_NAME) do
585     open_file(path, READ_CONTROL) do |handle|
586       FFI::MemoryPointer.new(:pointer, 1) do |owner_sid_ptr_ptr|
587         FFI::MemoryPointer.new(:pointer, 1) do |group_sid_ptr_ptr|
588           FFI::MemoryPointer.new(:pointer, 1) do |dacl_ptr_ptr|
589             FFI::MemoryPointer.new(:pointer, 1) do |sd_ptr_ptr|
590 
591               rv = GetSecurityInfo(
592                 handle,
593                 :SE_FILE_OBJECT,
594                 OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
595                 owner_sid_ptr_ptr,
596                 group_sid_ptr_ptr,
597                 dacl_ptr_ptr,
598                 FFI::Pointer::NULL, #sacl
599                 sd_ptr_ptr) #sec desc
600               raise Puppet::Util::Windows::Error.new(_("Failed to get security information")) if rv != FFI::ERROR_SUCCESS
601 
602               # these 2 convenience params are not freed since they point inside sd_ptr
603               owner = Puppet::Util::Windows::SID.sid_ptr_to_string(owner_sid_ptr_ptr.get_pointer(0))
604               group = Puppet::Util::Windows::SID.sid_ptr_to_string(group_sid_ptr_ptr.get_pointer(0))
605 
606               FFI::MemoryPointer.new(:word, 1) do |control|
607                 FFI::MemoryPointer.new(:dword, 1) do |revision|
608                   sd_ptr_ptr.read_win32_local_pointer do |sd_ptr|
609 
610                     if GetSecurityDescriptorControl(sd_ptr, control, revision) == FFI::WIN32_FALSE
611                       raise Puppet::Util::Windows::Error.new(_("Failed to get security descriptor control"))
612                     end
613 
614                     protect = (control.read_word & SE_DACL_PROTECTED) == SE_DACL_PROTECTED
615                     dacl = parse_dacl(dacl_ptr_ptr.get_pointer(0))
616                     sd = Puppet::Util::Windows::SecurityDescriptor.new(owner, group, dacl, protect)
617                   end
618                 end
619               end
620             end
621           end
622         end
623       end
624     end
625   end
626 
627   sd
628 end
open_file(path, access) { |handle| ... } click to toggle source

Open an existing file with the specified access mode, and execute a block with the opened file HANDLE.

    # File lib/puppet/util/windows/security.rb
512 def open_file(path, access, &block)
513   handle = CreateFileW(
514            wide_string(path),
515            access,
516            FILE::FILE_SHARE_READ | FILE::FILE_SHARE_WRITE,
517            FFI::Pointer::NULL, # security_attributes
518            FILE::OPEN_EXISTING,
519            FILE::FILE_FLAG_OPEN_REPARSE_POINT | FILE::FILE_FLAG_BACKUP_SEMANTICS,
520            FFI::Pointer::NULL_HANDLE) # template
521 
522   if handle == Puppet::Util::Windows::File::INVALID_HANDLE_VALUE
523     raise Puppet::Util::Windows::Error.new(_("Failed to open '%{path}'") % { path: path })
524   end
525 
526   begin
527     yield handle
528   ensure
529     FFI::WIN32.CloseHandle(handle) if handle
530   end
531 
532   # handle has already had CloseHandle called against it, nothing to return
533   nil
534 end
parse_dacl(dacl_ptr) click to toggle source
    # File lib/puppet/util/windows/security.rb
463 def parse_dacl(dacl_ptr)
464   # REMIND: need to handle NULL DACL
465   if IsValidAcl(dacl_ptr) == FFI::WIN32_FALSE
466     raise Puppet::Util::Windows::Error.new(_("Invalid DACL"))
467   end
468 
469   dacl_struct = ACL.new(dacl_ptr)
470   ace_count = dacl_struct[:AceCount]
471 
472   dacl = Puppet::Util::Windows::AccessControlList.new
473 
474   # deny all
475   return dacl if ace_count == 0
476 
477   0.upto(ace_count - 1) do |i|
478     FFI::MemoryPointer.new(:pointer, 1) do |ace_ptr|
479 
480       next if GetAce(dacl_ptr, i, ace_ptr) == FFI::WIN32_FALSE
481 
482       # ACE structures vary depending on the type. We are only concerned with
483       # ACCESS_ALLOWED_ACE and ACCESS_DENIED_ACEs, which have the same layout
484       ace = GENERIC_ACCESS_ACE.new(ace_ptr.get_pointer(0)) #deref LPVOID *
485 
486       ace_type = ace[:Header][:AceType]
487       if ace_type != Puppet::Util::Windows::AccessControlEntry::ACCESS_ALLOWED_ACE_TYPE &&
488         ace_type != Puppet::Util::Windows::AccessControlEntry::ACCESS_DENIED_ACE_TYPE
489         Puppet.warning _("Unsupported access control entry type: 0x%{type}") % { type: ace_type.to_s(16) }
490         next
491       end
492 
493       # using pointer addition gives the FFI::Pointer a size, but that's OK here
494       sid = Puppet::Util::Windows::SID.sid_ptr_to_string(ace.pointer + GENERIC_ACCESS_ACE.offset_of(:SidStart))
495       mask = ace[:Mask]
496       ace_flags = ace[:Header][:AceFlags]
497 
498       case ace_type
499       when Puppet::Util::Windows::AccessControlEntry::ACCESS_ALLOWED_ACE_TYPE
500         dacl.allow(sid, mask, ace_flags)
501       when Puppet::Util::Windows::AccessControlEntry::ACCESS_DENIED_ACE_TYPE
502         dacl.deny(sid, mask, ace_flags)
503       end
504     end
505   end
506 
507   dacl
508 end
set_group(group_sid, path) click to toggle source

Set the owner of the object referenced by path to the specified group_sid. The group sid should be of the form “S-1-5-32-544” and can either be a user or group. Any user with WRITE_OWNER access to the object can change the group (regardless of whether the current user belongs to that group or not).

    # File lib/puppet/util/windows/security.rb
143 def set_group(group_sid, path)
144   sd = get_security_descriptor(path)
145 
146   if group_sid != sd.group
147     sd.group = group_sid
148     set_security_descriptor(path, sd)
149   end
150 end
set_mode(mode, path, protected = true, managing_owner = false, managing_group = false) click to toggle source

Set the mode of the object referenced by path to the specified mode. The mode should be specified as POSIX-style read, write, and execute modes for the user, group, and other classes, e.g. 0640. The sticky bit, S_ISVTX, is supported, but is only meaningful for directories. If set, group and others are not allowed to delete child objects for which they are not the owner. By default, the DACL is set to protected, meaning it does not inherit access control entries from parent objects. This can be changed by setting protected to false. The owner of the object (with READ_CONTROL and WRITE_DACL access) can always change the mode. Only a user with the SE_BACKUP_NAME and SE_RESTORE_NAME privileges in their process token can change the mode for objects that they do not have read and write access to.

    # File lib/puppet/util/windows/security.rb
281 def set_mode(mode, path, protected = true, managing_owner = false, managing_group = false)
282   sd = get_security_descriptor(path)
283   well_known_world_sid = Puppet::Util::Windows::SID::Everyone
284   well_known_nobody_sid = Puppet::Util::Windows::SID::Nobody
285   well_known_system_sid = Puppet::Util::Windows::SID::LocalSystem
286 
287   owner_allow = FILE::STANDARD_RIGHTS_ALL  |
288     FILE::FILE_READ_ATTRIBUTES |
289     FILE::FILE_WRITE_ATTRIBUTES
290   # this prevents a mode that is not 7 from taking ownership of a file based
291   # on group membership and rewriting it / making it executable
292   group_allow = FILE::STANDARD_RIGHTS_READ |
293     FILE::FILE_READ_ATTRIBUTES |
294     FILE::SYNCHRONIZE
295   other_allow = FILE::STANDARD_RIGHTS_READ |
296     FILE::FILE_READ_ATTRIBUTES |
297     FILE::SYNCHRONIZE
298   nobody_allow = 0
299   system_allow = 0
300 
301   MODE_TO_MASK.each do |k,v|
302     if ((mode >> 6) & k) == k
303       owner_allow |= v
304     end
305     if ((mode >> 3) & k) == k
306       group_allow |= v
307     end
308     if (mode & k) == k
309       other_allow |= v
310     end
311   end
312 
313   # With a mode value of '7' for group / other, the value must then include
314   # additional perms beyond STANDARD_RIGHTS_READ to allow DACL modification
315   if ((mode & S_IRWXG) == S_IRWXG)
316     group_allow |= FILE::DELETE | FILE::WRITE_DAC | FILE::WRITE_OWNER
317   end
318   if ((mode & S_IRWXO) == S_IRWXO)
319     other_allow |= FILE::DELETE | FILE::WRITE_DAC | FILE::WRITE_OWNER
320   end
321 
322   if (mode & S_ISVTX).nonzero?
323     nobody_allow |= FILE::FILE_APPEND_DATA;
324   end
325 
326   isownergroup = sd.owner == sd.group
327 
328   # caller is NOT managing SYSTEM by using group or owner, so set to FULL
329   if ! [sd.owner, sd.group].include? well_known_system_sid
330     # we don't check S_ISYSTEM_MISSING bit, but automatically carry over existing SYSTEM perms
331     # by default set SYSTEM perms to full
332     system_allow = FILE::FILE_ALL_ACCESS
333   else
334     # It is possible to set SYSTEM with a mode other than Full Control (7) however this makes no sense and in practical terms
335     # should not be done.  We can trap these instances and correct them before being applied.
336     if (sd.owner == well_known_system_sid) && (owner_allow != FILE::FILE_ALL_ACCESS)
337       # If owner and group are both SYSTEM but group is unmanaged the control rights of system will be set to FullControl by
338       # the unmanaged group, so there is no need for the warning
339       if managing_owner && (!isownergroup || managing_group)
340         #TRANSLATORS 'SYSTEM' is a Windows name and should not be translated
341         Puppet.warning _("Setting control rights for %{path} owner SYSTEM to less than Full Control rights. Setting SYSTEM rights to less than Full Control may have unintented consequences for operations on this file") % { path: path }
342       elsif managing_owner && isownergroup
343         #TRANSLATORS 'SYSTEM' is a Windows name and should not be translated
344         Puppet.debug { _("%{path} owner and group both set to user SYSTEM, but group is not managed directly: SYSTEM user rights will be set to FullControl by group") % { path: path } }
345       else
346         #TRANSLATORS 'SYSTEM' is a Windows name and should not be translated
347         Puppet.debug { _("An attempt to set mode %{mode} on item %{path} would result in the owner, SYSTEM, to have less than Full Control rights. This attempt has been corrected to Full Control") % { mode: mode.to_s(8), path: path } }
348         owner_allow = FILE::FILE_ALL_ACCESS
349       end
350     end
351 
352     if (sd.group == well_known_system_sid) && (group_allow != FILE::FILE_ALL_ACCESS)
353       # If owner and group are both SYSTEM but owner is unmanaged the control rights of system will be set to FullControl by
354       # the unmanaged owner, so there is no need for the warning.
355       if managing_group && (!isownergroup || managing_owner)
356         #TRANSLATORS 'SYSTEM' is a Windows name and should not be translated
357         Puppet.warning _("Setting control rights for %{path} group SYSTEM to less than Full Control rights. Setting SYSTEM rights to less than Full Control may have unintented consequences for operations on this file") % { path: path }
358       elsif managing_group && isownergroup
359         #TRANSLATORS 'SYSTEM' is a Windows name and should not be translated
360         Puppet.debug { _("%{path} owner and group both set to user SYSTEM, but owner is not managed directly: SYSTEM user rights will be set to FullControl by owner") % { path: path } }
361       else
362         #TRANSLATORS 'SYSTEM' is a Windows name and should not be translated
363         Puppet.debug { _("An attempt to set mode %{mode} on item %{path} would result in the group, SYSTEM, to have less than Full Control rights. This attempt has been corrected to Full Control") % { mode: mode.to_s(8), path: path } }
364         group_allow = FILE::FILE_ALL_ACCESS
365       end
366     end
367   end
368 
369   # even though FILE_DELETE_CHILD only applies to directories, it can be set on files
370   # this is necessary to do to ensure a file ends up with (F) FullControl
371   if (mode & (S_IWUSR | S_IXUSR)) == (S_IWUSR | S_IXUSR)
372     owner_allow |= FILE::FILE_DELETE_CHILD
373   end
374   if (mode & (S_IWGRP | S_IXGRP)) == (S_IWGRP | S_IXGRP) && (mode & S_ISVTX) == 0
375     group_allow |= FILE::FILE_DELETE_CHILD
376   end
377   if (mode & (S_IWOTH | S_IXOTH)) == (S_IWOTH | S_IXOTH) && (mode & S_ISVTX) == 0
378     other_allow |= FILE::FILE_DELETE_CHILD
379   end
380 
381   # if owner and group the same, then map group permissions to the one owner ACE
382   if isownergroup
383     owner_allow |= group_allow
384   end
385 
386   # if any ACE allows write, then clear readonly bit, but do this before we overwrite
387   # the DACl and lose our ability to set the attribute
388   if ((owner_allow | group_allow | other_allow ) & FILE::FILE_WRITE_DATA) == FILE::FILE_WRITE_DATA
389     FILE.remove_attributes(path, FILE::FILE_ATTRIBUTE_READONLY)
390   end
391 
392   isdir = File.directory?(path)
393   dacl = Puppet::Util::Windows::AccessControlList.new
394   dacl.allow(sd.owner, owner_allow)
395   unless isownergroup
396     dacl.allow(sd.group, group_allow)
397   end
398   dacl.allow(well_known_world_sid, other_allow)
399   dacl.allow(well_known_nobody_sid, nobody_allow)
400 
401   # TODO: system should be first?
402   flags = !isdir ? 0 :
403     Puppet::Util::Windows::AccessControlEntry::CONTAINER_INHERIT_ACE |
404     Puppet::Util::Windows::AccessControlEntry::OBJECT_INHERIT_ACE
405   dacl.allow(well_known_system_sid, system_allow, flags)
406 
407   # add inherit-only aces for child dirs and files that are created within the dir
408   inherit_only = Puppet::Util::Windows::AccessControlEntry::INHERIT_ONLY_ACE
409   if isdir
410     inherit = inherit_only | Puppet::Util::Windows::AccessControlEntry::CONTAINER_INHERIT_ACE
411     dacl.allow(Puppet::Util::Windows::SID::CreatorOwner, owner_allow, inherit)
412     dacl.allow(Puppet::Util::Windows::SID::CreatorGroup, group_allow, inherit)
413 
414     inherit = inherit_only | Puppet::Util::Windows::AccessControlEntry::OBJECT_INHERIT_ACE
415     # allow any previously set bits *except* for these
416     perms_to_strip = ~(FILE::FILE_EXECUTE + FILE::WRITE_OWNER + FILE::WRITE_DAC)
417     dacl.allow(Puppet::Util::Windows::SID::CreatorOwner, owner_allow & perms_to_strip, inherit)
418     dacl.allow(Puppet::Util::Windows::SID::CreatorGroup, group_allow & perms_to_strip, inherit)
419   end
420 
421   new_sd = Puppet::Util::Windows::SecurityDescriptor.new(sd.owner, sd.group, dacl, protected)
422   set_security_descriptor(path, new_sd)
423 
424   nil
425 end
set_owner(owner_sid, path) click to toggle source

Set the owner of the object referenced by path to the specified owner_sid. The owner sid should be of the form “S-1-5-32-544” and can either be a user or group. Only a user with the SE_RESTORE_NAME privilege in their process token can overwrite the object's owner to something other than the current user.

    # File lib/puppet/util/windows/security.rb
118 def set_owner(owner_sid, path)
119   sd = get_security_descriptor(path)
120 
121   if owner_sid != sd.owner
122     sd.owner = owner_sid
123     set_security_descriptor(path, sd)
124   end
125 end
set_privilege(privilege, enable) click to toggle source

Enable or disable a privilege. Note this doesn't add any privileges the user doesn't already has, it just enables privileges that are disabled.

    # File lib/puppet/util/windows/security.rb
549 def set_privilege(privilege, enable)
550   return unless Puppet.features.root?
551 
552   Puppet::Util::Windows::Process.with_process_token(TOKEN_ADJUST_PRIVILEGES) do |token|
553     Puppet::Util::Windows::Process.lookup_privilege_value(privilege) do |luid|
554       FFI::MemoryPointer.new(Puppet::Util::Windows::Process::LUID_AND_ATTRIBUTES.size) do |luid_and_attributes_ptr|
555         # allocate unmanaged memory for structs that we clean up afterwards
556         luid_and_attributes = Puppet::Util::Windows::Process::LUID_AND_ATTRIBUTES.new(luid_and_attributes_ptr)
557         luid_and_attributes[:Luid] = luid
558         luid_and_attributes[:Attributes] = enable ? SE_PRIVILEGE_ENABLED : 0
559 
560         FFI::MemoryPointer.new(Puppet::Util::Windows::Process::TOKEN_PRIVILEGES.size) do |token_privileges_ptr|
561           token_privileges = Puppet::Util::Windows::Process::TOKEN_PRIVILEGES.new(token_privileges_ptr)
562           token_privileges[:PrivilegeCount] = 1
563           token_privileges[:Privileges][0] = luid_and_attributes
564 
565           # size is correct given we only have 1 LUID, otherwise would be:
566           # [:PrivilegeCount].size + [:PrivilegeCount] * LUID_AND_ATTRIBUTES.size
567           if AdjustTokenPrivileges(token, FFI::WIN32_FALSE,
568               token_privileges, token_privileges.size,
569               FFI::MemoryPointer::NULL, FFI::MemoryPointer::NULL) == FFI::WIN32_FALSE
570             raise Puppet::Util::Windows::Error.new(_("Failed to adjust process privileges"))
571           end
572         end
573       end
574     end
575   end
576 
577   # token / luid structs freed by this point, so return true as nothing raised
578   true
579 end
set_security_descriptor(path, sd) click to toggle source

setting DACL requires both READ_CONTROL and WRITE_DACL access rights, and their respective privileges, SE_BACKUP_NAME and SE_RESTORE_NAME.

    # File lib/puppet/util/windows/security.rb
641 def set_security_descriptor(path, sd)
642   FFI::MemoryPointer.new(:byte, get_max_generic_acl_size(sd.dacl.count)) do |acl_ptr|
643     if InitializeAcl(acl_ptr, acl_ptr.size, ACL_REVISION) == FFI::WIN32_FALSE
644       raise Puppet::Util::Windows::Error.new(_("Failed to initialize ACL"))
645     end
646 
647     if IsValidAcl(acl_ptr) == FFI::WIN32_FALSE
648       raise Puppet::Util::Windows::Error.new(_("Invalid DACL"))
649     end
650 
651     with_privilege(SE_BACKUP_NAME) do
652       with_privilege(SE_RESTORE_NAME) do
653         open_file(path, READ_CONTROL | WRITE_DAC | WRITE_OWNER) do |handle|
654           Puppet::Util::Windows::SID.string_to_sid_ptr(sd.owner) do |owner_sid_ptr|
655             Puppet::Util::Windows::SID.string_to_sid_ptr(sd.group) do |group_sid_ptr|
656               sd.dacl.each do |ace|
657                 case ace.type
658                 when Puppet::Util::Windows::AccessControlEntry::ACCESS_ALLOWED_ACE_TYPE
659                   #puts "ace: allow, sid #{Puppet::Util::Windows::SID.sid_to_name(ace.sid)}, mask 0x#{ace.mask.to_s(16)}"
660                   add_access_allowed_ace(acl_ptr, ace.mask, ace.sid, ace.flags)
661                 when Puppet::Util::Windows::AccessControlEntry::ACCESS_DENIED_ACE_TYPE
662                   #puts "ace: deny, sid #{Puppet::Util::Windows::SID.sid_to_name(ace.sid)}, mask 0x#{ace.mask.to_s(16)}"
663                   add_access_denied_ace(acl_ptr, ace.mask, ace.sid, ace.flags)
664                 else
665                   raise "We should never get here"
666                   # TODO: this should have been a warning in an earlier commit
667                 end
668               end
669 
670               # protected means the object does not inherit aces from its parent
671               flags = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION
672               flags |= sd.protect ? PROTECTED_DACL_SECURITY_INFORMATION : UNPROTECTED_DACL_SECURITY_INFORMATION
673 
674               rv = SetSecurityInfo(handle,
675                                    :SE_FILE_OBJECT,
676                                    flags,
677                                    owner_sid_ptr,
678                                    group_sid_ptr,
679                                    acl_ptr,
680                                    FFI::MemoryPointer::NULL)
681 
682               if rv != FFI::ERROR_SUCCESS
683                 raise Puppet::Util::Windows::Error.new(_("Failed to set security information"))
684               end
685             end
686           end
687         end
688       end
689     end
690   end
691 end
supports_acl?(path) click to toggle source
    # File lib/puppet/util/windows/security.rb
165 def supports_acl?(path)
166   supported = false
167   root = Pathname.new(path).enum_for(:ascend).to_a.last.to_s
168   # 'A trailing backslash is required'
169   root = "#{root}\\" unless root =~ /[\/\\]$/
170 
171   FFI::MemoryPointer.new(:pointer, 1) do |flags_ptr|
172     if GetVolumeInformationW(wide_string(root), FFI::Pointer::NULL, 0,
173         FFI::Pointer::NULL, FFI::Pointer::NULL,
174         flags_ptr, FFI::Pointer::NULL, 0) == FFI::WIN32_FALSE
175       raise Puppet::Util::Windows::Error.new(_("Failed to get volume information"))
176     end
177     supported = flags_ptr.read_dword & FILE_PERSISTENT_ACLS == FILE_PERSISTENT_ACLS
178   end
179 
180   supported
181 end
with_privilege(privilege) { || ... } click to toggle source

Execute a block with the specified privilege enabled

    # File lib/puppet/util/windows/security.rb
537 def with_privilege(privilege, &block)
538   set_privilege(privilege, true)
539   yield
540 ensure
541   set_privilege(privilege, false)
542 end