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 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
# 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
# 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 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
# 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 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 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
# 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 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
# 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 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 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 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
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
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
# 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
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