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
- 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
# File lib/puppet/util/windows/security.rb 428 def add_access_allowed_ace(acl, mask, sid, inherit = nil) 429 inherit ||= NO_INHERITANCE 430 431 Puppet::Util::Windows::SID.string_to_sid_ptr(sid) do |sid_ptr| 432 if Puppet::Util::Windows::SID.IsValidSid(sid_ptr) == FFI::WIN32_FALSE 433 raise Puppet::Util::Windows::Error.new(_("Invalid SID")) 434 end 435 436 if AddAccessAllowedAceEx(acl, ACL_REVISION, inherit, mask, sid_ptr) == FFI::WIN32_FALSE 437 raise Puppet::Util::Windows::Error.new(_("Failed to add access control entry")) 438 end 439 end 440 441 # ensure this method is void if it doesn't raise 442 nil 443 end
# File lib/puppet/util/windows/security.rb 445 def add_access_denied_ace(acl, mask, sid, inherit = nil) 446 inherit ||= NO_INHERITANCE 447 448 Puppet::Util::Windows::SID.string_to_sid_ptr(sid) do |sid_ptr| 449 if Puppet::Util::Windows::SID.IsValidSid(sid_ptr) == FFI::WIN32_FALSE 450 raise Puppet::Util::Windows::Error.new(_("Invalid SID")) 451 end 452 453 if AddAccessDeniedAceEx(acl, ACL_REVISION, inherit, mask, sid_ptr) == FFI::WIN32_FALSE 454 raise Puppet::Util::Windows::Error.new(_("Failed to add access control entry")) 455 end 456 end 457 458 # ensure this method is void if it doesn't raise 459 nil 460 end
# File lib/puppet/util/windows/security.rb 188 def get_aces_for_path_by_sid(path, sid) 189 get_security_descriptor(path).dacl.select { |ace| ace.sid == sid } 190 end
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 156 def get_group(path) 157 return unless supports_acl?(path) 158 159 get_security_descriptor(path).group 160 end
# File lib/puppet/util/windows/security.rb 629 def get_max_generic_acl_size(ace_count) 630 # https://msdn.microsoft.com/en-us/library/windows/desktop/aa378853(v=vs.85).aspx 631 # To calculate the initial size of an ACL, add the following together, and then align the result to the nearest DWORD: 632 # * Size of the ACL structure. 633 # * Size of each ACE structure that the ACL is to contain minus the SidStart member (DWORD) of the ACE. 634 # * Length of the SID that each ACE is to contain. 635 ACL.size + ace_count * MAXIMUM_GENERIC_ACE_SIZE 636 end
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 198 def get_mode(path) 199 return unless supports_acl?(path) 200 201 well_known_world_sid = Puppet::Util::Windows::SID::Everyone 202 well_known_nobody_sid = Puppet::Util::Windows::SID::Nobody 203 well_known_system_sid = Puppet::Util::Windows::SID::LocalSystem 204 well_known_app_packages_sid = Puppet::Util::Windows::SID::AllAppPackages 205 206 mode = S_ISYSTEM_MISSING 207 208 sd = get_security_descriptor(path) 209 sd.dacl.each do |ace| 210 next if ace.inherit_only? 211 212 case ace.sid 213 when sd.owner 214 MASK_TO_MODE.each_pair do |k,v| 215 if (ace.mask & k) == k 216 mode |= (v << 6) 217 end 218 end 219 when sd.group 220 MASK_TO_MODE.each_pair do |k,v| 221 if (ace.mask & k) == k 222 mode |= (v << 3) 223 end 224 end 225 when well_known_world_sid 226 MASK_TO_MODE.each_pair do |k,v| 227 if (ace.mask & k) == k 228 mode |= (v << 6) | (v << 3) | v 229 end 230 end 231 if File.directory?(path) && 232 (ace.mask & (FILE::FILE_WRITE_DATA | FILE::FILE_EXECUTE | FILE::FILE_DELETE_CHILD)) == (FILE::FILE_WRITE_DATA | FILE::FILE_EXECUTE) 233 mode |= S_ISVTX; 234 end 235 when well_known_nobody_sid 236 if (ace.mask & FILE::FILE_APPEND_DATA).nonzero? 237 mode |= S_ISVTX 238 end 239 when well_known_app_packages_sid 240 when well_known_system_sid 241 else 242 #puts "Warning, unable to map SID into POSIX mode: #{ace.sid}" 243 mode |= S_IEXTRA 244 end 245 246 if ace.sid == well_known_system_sid 247 mode &= ~S_ISYSTEM_MISSING 248 end 249 250 # if owner and group the same, then user and group modes are the OR of both 251 if sd.owner == sd.group 252 mode |= ((mode & S_IRWXG) << 3) | ((mode & S_IRWXU) >> 3) 253 #puts "owner: #{sd.group}, 0x#{ace.mask.to_s(16)}, #{mode.to_s(8)}" 254 end 255 end 256 257 #puts "get_mode: #{mode.to_s(8)}" 258 mode 259 end
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 131 def get_owner(path) 132 return unless supports_acl?(path) 133 134 get_security_descriptor(path).owner 135 end
# File lib/puppet/util/windows/security.rb 580 def get_security_descriptor(path) 581 sd = nil 582 583 with_privilege(SE_BACKUP_NAME) do 584 open_file(path, READ_CONTROL) do |handle| 585 FFI::MemoryPointer.new(:pointer, 1) do |owner_sid_ptr_ptr| 586 FFI::MemoryPointer.new(:pointer, 1) do |group_sid_ptr_ptr| 587 FFI::MemoryPointer.new(:pointer, 1) do |dacl_ptr_ptr| 588 FFI::MemoryPointer.new(:pointer, 1) do |sd_ptr_ptr| 589 590 rv = GetSecurityInfo( 591 handle, 592 :SE_FILE_OBJECT, 593 OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, 594 owner_sid_ptr_ptr, 595 group_sid_ptr_ptr, 596 dacl_ptr_ptr, 597 FFI::Pointer::NULL, #sacl 598 sd_ptr_ptr) #sec desc 599 raise Puppet::Util::Windows::Error.new(_("Failed to get security information")) if rv != FFI::ERROR_SUCCESS 600 601 # these 2 convenience params are not freed since they point inside sd_ptr 602 owner = Puppet::Util::Windows::SID.sid_ptr_to_string(owner_sid_ptr_ptr.get_pointer(0)) 603 group = Puppet::Util::Windows::SID.sid_ptr_to_string(group_sid_ptr_ptr.get_pointer(0)) 604 605 FFI::MemoryPointer.new(:word, 1) do |control| 606 FFI::MemoryPointer.new(:dword, 1) do |revision| 607 sd_ptr_ptr.read_win32_local_pointer do |sd_ptr| 608 609 if GetSecurityDescriptorControl(sd_ptr, control, revision) == FFI::WIN32_FALSE 610 raise Puppet::Util::Windows::Error.new(_("Failed to get security descriptor control")) 611 end 612 613 protect = (control.read_word & SE_DACL_PROTECTED) == SE_DACL_PROTECTED 614 dacl = parse_dacl(dacl_ptr_ptr.get_pointer(0)) 615 sd = Puppet::Util::Windows::SecurityDescriptor.new(owner, group, dacl, protect) 616 end 617 end 618 end 619 end 620 end 621 end 622 end 623 end 624 end 625 626 sd 627 end
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 511 def open_file(path, access, &block) 512 handle = CreateFileW( 513 wide_string(path), 514 access, 515 FILE::FILE_SHARE_READ | FILE::FILE_SHARE_WRITE, 516 FFI::Pointer::NULL, # security_attributes 517 FILE::OPEN_EXISTING, 518 FILE::FILE_FLAG_OPEN_REPARSE_POINT | FILE::FILE_FLAG_BACKUP_SEMANTICS, 519 FFI::Pointer::NULL_HANDLE) # template 520 521 if handle == Puppet::Util::Windows::File::INVALID_HANDLE_VALUE 522 raise Puppet::Util::Windows::Error.new(_("Failed to open '%{path}'") % { path: path }) 523 end 524 525 begin 526 yield handle 527 ensure 528 FFI::WIN32.CloseHandle(handle) if handle 529 end 530 531 # handle has already had CloseHandle called against it, nothing to return 532 nil 533 end
# File lib/puppet/util/windows/security.rb 462 def parse_dacl(dacl_ptr) 463 # REMIND: need to handle NULL DACL 464 if IsValidAcl(dacl_ptr) == FFI::WIN32_FALSE 465 raise Puppet::Util::Windows::Error.new(_("Invalid DACL")) 466 end 467 468 dacl_struct = ACL.new(dacl_ptr) 469 ace_count = dacl_struct[:AceCount] 470 471 dacl = Puppet::Util::Windows::AccessControlList.new 472 473 # deny all 474 return dacl if ace_count == 0 475 476 0.upto(ace_count - 1) do |i| 477 FFI::MemoryPointer.new(:pointer, 1) do |ace_ptr| 478 479 next if GetAce(dacl_ptr, i, ace_ptr) == FFI::WIN32_FALSE 480 481 # ACE structures vary depending on the type. We are only concerned with 482 # ACCESS_ALLOWED_ACE and ACCESS_DENIED_ACEs, which have the same layout 483 ace = GENERIC_ACCESS_ACE.new(ace_ptr.get_pointer(0)) #deref LPVOID * 484 485 ace_type = ace[:Header][:AceType] 486 if ace_type != Puppet::Util::Windows::AccessControlEntry::ACCESS_ALLOWED_ACE_TYPE && 487 ace_type != Puppet::Util::Windows::AccessControlEntry::ACCESS_DENIED_ACE_TYPE 488 Puppet.warning _("Unsupported access control entry type: 0x%{type}") % { type: ace_type.to_s(16) } 489 next 490 end 491 492 # using pointer addition gives the FFI::Pointer a size, but that's OK here 493 sid = Puppet::Util::Windows::SID.sid_ptr_to_string(ace.pointer + GENERIC_ACCESS_ACE.offset_of(:SidStart)) 494 mask = ace[:Mask] 495 ace_flags = ace[:Header][:AceFlags] 496 497 case ace_type 498 when Puppet::Util::Windows::AccessControlEntry::ACCESS_ALLOWED_ACE_TYPE 499 dacl.allow(sid, mask, ace_flags) 500 when Puppet::Util::Windows::AccessControlEntry::ACCESS_DENIED_ACE_TYPE 501 dacl.deny(sid, mask, ace_flags) 502 end 503 end 504 end 505 506 dacl 507 end
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 142 def set_group(group_sid, path) 143 sd = get_security_descriptor(path) 144 145 if group_sid != sd.group 146 sd.group = group_sid 147 set_security_descriptor(path, sd) 148 end 149 end
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 280 def set_mode(mode, path, protected = true, managing_owner = false, managing_group = false) 281 sd = get_security_descriptor(path) 282 well_known_world_sid = Puppet::Util::Windows::SID::Everyone 283 well_known_nobody_sid = Puppet::Util::Windows::SID::Nobody 284 well_known_system_sid = Puppet::Util::Windows::SID::LocalSystem 285 286 owner_allow = FILE::STANDARD_RIGHTS_ALL | 287 FILE::FILE_READ_ATTRIBUTES | 288 FILE::FILE_WRITE_ATTRIBUTES 289 # this prevents a mode that is not 7 from taking ownership of a file based 290 # on group membership and rewriting it / making it executable 291 group_allow = FILE::STANDARD_RIGHTS_READ | 292 FILE::FILE_READ_ATTRIBUTES | 293 FILE::SYNCHRONIZE 294 other_allow = FILE::STANDARD_RIGHTS_READ | 295 FILE::FILE_READ_ATTRIBUTES | 296 FILE::SYNCHRONIZE 297 nobody_allow = 0 298 system_allow = 0 299 300 MODE_TO_MASK.each do |k,v| 301 if ((mode >> 6) & k) == k 302 owner_allow |= v 303 end 304 if ((mode >> 3) & k) == k 305 group_allow |= v 306 end 307 if (mode & k) == k 308 other_allow |= v 309 end 310 end 311 312 # With a mode value of '7' for group / other, the value must then include 313 # additional perms beyond STANDARD_RIGHTS_READ to allow DACL modification 314 if ((mode & S_IRWXG) == S_IRWXG) 315 group_allow |= FILE::DELETE | FILE::WRITE_DAC | FILE::WRITE_OWNER 316 end 317 if ((mode & S_IRWXO) == S_IRWXO) 318 other_allow |= FILE::DELETE | FILE::WRITE_DAC | FILE::WRITE_OWNER 319 end 320 321 if (mode & S_ISVTX).nonzero? 322 nobody_allow |= FILE::FILE_APPEND_DATA; 323 end 324 325 isownergroup = sd.owner == sd.group 326 327 # caller is NOT managing SYSTEM by using group or owner, so set to FULL 328 if ! [sd.owner, sd.group].include? well_known_system_sid 329 # we don't check S_ISYSTEM_MISSING bit, but automatically carry over existing SYSTEM perms 330 # by default set SYSTEM perms to full 331 system_allow = FILE::FILE_ALL_ACCESS 332 else 333 # It is possible to set SYSTEM with a mode other than Full Control (7) however this makes no sense and in practical terms 334 # should not be done. We can trap these instances and correct them before being applied. 335 if (sd.owner == well_known_system_sid) && (owner_allow != FILE::FILE_ALL_ACCESS) 336 # If owner and group are both SYSTEM but group is unmanaged the control rights of system will be set to FullControl by 337 # the unmanaged group, so there is no need for the warning 338 if managing_owner && (!isownergroup || managing_group) 339 #TRANSLATORS 'SYSTEM' is a Windows name and should not be translated 340 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 } 341 elsif managing_owner && isownergroup 342 #TRANSLATORS 'SYSTEM' is a Windows name and should not be translated 343 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 } } 344 else 345 #TRANSLATORS 'SYSTEM' is a Windows name and should not be translated 346 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 } } 347 owner_allow = FILE::FILE_ALL_ACCESS 348 end 349 end 350 351 if (sd.group == well_known_system_sid) && (group_allow != FILE::FILE_ALL_ACCESS) 352 # If owner and group are both SYSTEM but owner is unmanaged the control rights of system will be set to FullControl by 353 # the unmanaged owner, so there is no need for the warning. 354 if managing_group && (!isownergroup || managing_owner) 355 #TRANSLATORS 'SYSTEM' is a Windows name and should not be translated 356 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 } 357 elsif managing_group && isownergroup 358 #TRANSLATORS 'SYSTEM' is a Windows name and should not be translated 359 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 } } 360 else 361 #TRANSLATORS 'SYSTEM' is a Windows name and should not be translated 362 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 } } 363 group_allow = FILE::FILE_ALL_ACCESS 364 end 365 end 366 end 367 368 # even though FILE_DELETE_CHILD only applies to directories, it can be set on files 369 # this is necessary to do to ensure a file ends up with (F) FullControl 370 if (mode & (S_IWUSR | S_IXUSR)) == (S_IWUSR | S_IXUSR) 371 owner_allow |= FILE::FILE_DELETE_CHILD 372 end 373 if (mode & (S_IWGRP | S_IXGRP)) == (S_IWGRP | S_IXGRP) && (mode & S_ISVTX) == 0 374 group_allow |= FILE::FILE_DELETE_CHILD 375 end 376 if (mode & (S_IWOTH | S_IXOTH)) == (S_IWOTH | S_IXOTH) && (mode & S_ISVTX) == 0 377 other_allow |= FILE::FILE_DELETE_CHILD 378 end 379 380 # if owner and group the same, then map group permissions to the one owner ACE 381 if isownergroup 382 owner_allow |= group_allow 383 end 384 385 # if any ACE allows write, then clear readonly bit, but do this before we overwrite 386 # the DACl and lose our ability to set the attribute 387 if ((owner_allow | group_allow | other_allow ) & FILE::FILE_WRITE_DATA) == FILE::FILE_WRITE_DATA 388 FILE.remove_attributes(path, FILE::FILE_ATTRIBUTE_READONLY) 389 end 390 391 isdir = File.directory?(path) 392 dacl = Puppet::Util::Windows::AccessControlList.new 393 dacl.allow(sd.owner, owner_allow) 394 unless isownergroup 395 dacl.allow(sd.group, group_allow) 396 end 397 dacl.allow(well_known_world_sid, other_allow) 398 dacl.allow(well_known_nobody_sid, nobody_allow) 399 400 # TODO: system should be first? 401 flags = !isdir ? 0 : 402 Puppet::Util::Windows::AccessControlEntry::CONTAINER_INHERIT_ACE | 403 Puppet::Util::Windows::AccessControlEntry::OBJECT_INHERIT_ACE 404 dacl.allow(well_known_system_sid, system_allow, flags) 405 406 # add inherit-only aces for child dirs and files that are created within the dir 407 inherit_only = Puppet::Util::Windows::AccessControlEntry::INHERIT_ONLY_ACE 408 if isdir 409 inherit = inherit_only | Puppet::Util::Windows::AccessControlEntry::CONTAINER_INHERIT_ACE 410 dacl.allow(Puppet::Util::Windows::SID::CreatorOwner, owner_allow, inherit) 411 dacl.allow(Puppet::Util::Windows::SID::CreatorGroup, group_allow, inherit) 412 413 inherit = inherit_only | Puppet::Util::Windows::AccessControlEntry::OBJECT_INHERIT_ACE 414 # allow any previously set bits *except* for these 415 perms_to_strip = ~(FILE::FILE_EXECUTE + FILE::WRITE_OWNER + FILE::WRITE_DAC) 416 dacl.allow(Puppet::Util::Windows::SID::CreatorOwner, owner_allow & perms_to_strip, inherit) 417 dacl.allow(Puppet::Util::Windows::SID::CreatorGroup, group_allow & perms_to_strip, inherit) 418 end 419 420 new_sd = Puppet::Util::Windows::SecurityDescriptor.new(sd.owner, sd.group, dacl, protected) 421 set_security_descriptor(path, new_sd) 422 423 nil 424 end
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 117 def set_owner(owner_sid, path) 118 sd = get_security_descriptor(path) 119 120 if owner_sid != sd.owner 121 sd.owner = owner_sid 122 set_security_descriptor(path, sd) 123 end 124 end
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 548 def set_privilege(privilege, enable) 549 return unless Puppet.features.root? 550 551 Puppet::Util::Windows::Process.with_process_token(TOKEN_ADJUST_PRIVILEGES) do |token| 552 Puppet::Util::Windows::Process.lookup_privilege_value(privilege) do |luid| 553 FFI::MemoryPointer.new(Puppet::Util::Windows::Process::LUID_AND_ATTRIBUTES.size) do |luid_and_attributes_ptr| 554 # allocate unmanaged memory for structs that we clean up afterwards 555 luid_and_attributes = Puppet::Util::Windows::Process::LUID_AND_ATTRIBUTES.new(luid_and_attributes_ptr) 556 luid_and_attributes[:Luid] = luid 557 luid_and_attributes[:Attributes] = enable ? SE_PRIVILEGE_ENABLED : 0 558 559 FFI::MemoryPointer.new(Puppet::Util::Windows::Process::TOKEN_PRIVILEGES.size) do |token_privileges_ptr| 560 token_privileges = Puppet::Util::Windows::Process::TOKEN_PRIVILEGES.new(token_privileges_ptr) 561 token_privileges[:PrivilegeCount] = 1 562 token_privileges[:Privileges][0] = luid_and_attributes 563 564 # size is correct given we only have 1 LUID, otherwise would be: 565 # [:PrivilegeCount].size + [:PrivilegeCount] * LUID_AND_ATTRIBUTES.size 566 if AdjustTokenPrivileges(token, FFI::WIN32_FALSE, 567 token_privileges, token_privileges.size, 568 FFI::MemoryPointer::NULL, FFI::MemoryPointer::NULL) == FFI::WIN32_FALSE 569 raise Puppet::Util::Windows::Error.new(_("Failed to adjust process privileges")) 570 end 571 end 572 end 573 end 574 end 575 576 # token / luid structs freed by this point, so return true as nothing raised 577 true 578 end
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 640 def set_security_descriptor(path, sd) 641 FFI::MemoryPointer.new(:byte, get_max_generic_acl_size(sd.dacl.count)) do |acl_ptr| 642 if InitializeAcl(acl_ptr, acl_ptr.size, ACL_REVISION) == FFI::WIN32_FALSE 643 raise Puppet::Util::Windows::Error.new(_("Failed to initialize ACL")) 644 end 645 646 if IsValidAcl(acl_ptr) == FFI::WIN32_FALSE 647 raise Puppet::Util::Windows::Error.new(_("Invalid DACL")) 648 end 649 650 with_privilege(SE_BACKUP_NAME) do 651 with_privilege(SE_RESTORE_NAME) do 652 open_file(path, READ_CONTROL | WRITE_DAC | WRITE_OWNER) do |handle| 653 Puppet::Util::Windows::SID.string_to_sid_ptr(sd.owner) do |owner_sid_ptr| 654 Puppet::Util::Windows::SID.string_to_sid_ptr(sd.group) do |group_sid_ptr| 655 sd.dacl.each do |ace| 656 case ace.type 657 when Puppet::Util::Windows::AccessControlEntry::ACCESS_ALLOWED_ACE_TYPE 658 #puts "ace: allow, sid #{Puppet::Util::Windows::SID.sid_to_name(ace.sid)}, mask 0x#{ace.mask.to_s(16)}" 659 add_access_allowed_ace(acl_ptr, ace.mask, ace.sid, ace.flags) 660 when Puppet::Util::Windows::AccessControlEntry::ACCESS_DENIED_ACE_TYPE 661 #puts "ace: deny, sid #{Puppet::Util::Windows::SID.sid_to_name(ace.sid)}, mask 0x#{ace.mask.to_s(16)}" 662 add_access_denied_ace(acl_ptr, ace.mask, ace.sid, ace.flags) 663 else 664 raise "We should never get here" 665 # TODO: this should have been a warning in an earlier commit 666 end 667 end 668 669 # protected means the object does not inherit aces from its parent 670 flags = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION 671 flags |= sd.protect ? PROTECTED_DACL_SECURITY_INFORMATION : UNPROTECTED_DACL_SECURITY_INFORMATION 672 673 rv = SetSecurityInfo(handle, 674 :SE_FILE_OBJECT, 675 flags, 676 owner_sid_ptr, 677 group_sid_ptr, 678 acl_ptr, 679 FFI::MemoryPointer::NULL) 680 681 if rv != FFI::ERROR_SUCCESS 682 raise Puppet::Util::Windows::Error.new(_("Failed to set security information")) 683 end 684 end 685 end 686 end 687 end 688 end 689 end 690 end
# File lib/puppet/util/windows/security.rb 164 def supports_acl?(path) 165 supported = false 166 root = Pathname.new(path).enum_for(:ascend).to_a.last.to_s 167 # 'A trailing backslash is required' 168 root = "#{root}\\" unless root =~ /[\/\\]$/ 169 170 FFI::MemoryPointer.new(:pointer, 1) do |flags_ptr| 171 if GetVolumeInformationW(wide_string(root), FFI::Pointer::NULL, 0, 172 FFI::Pointer::NULL, FFI::Pointer::NULL, 173 flags_ptr, FFI::Pointer::NULL, 0) == FFI::WIN32_FALSE 174 raise Puppet::Util::Windows::Error.new(_("Failed to get volume information")) 175 end 176 supported = flags_ptr.read_dword & FILE_PERSISTENT_ACLS == FILE_PERSISTENT_ACLS 177 end 178 179 supported 180 end
Execute a block with the specified privilege enabled
# File lib/puppet/util/windows/security.rb 536 def with_privilege(privilege, &block) 537 set_privilege(privilege, true) 538 yield 539 ensure 540 set_privilege(privilege, false) 541 end