FILE: C:\Users\All Users\PuppetLabs\puppet\var\lib\puppet\provider\acl\windows\base.rb
--
require 'pathname'
class Puppet::Provider::Acl
module Windows
# This provides the detailed implementation details for the
# provider and should shield the provider from legacy
# support implementations that would happen here.
module Base
if Puppet::Util::Platform.windows?
require Pathname.new(__FILE__).dirname + '../../../../' + 'puppet/type/acl/ace'
require 'puppet/util/windows/security'
require 'win32/security'
# fixes come after everything else is loaded
require Pathname.new(__FILE__).dirname + '../../../../' + 'puppet/util/monkey_patches'
REFRESH_SD = true
DO_NOT_REFRESH_SD = false
GENERIC_ALL = 0x10000000
GENERIC_WRITE = 0x40000000
GENERIC_READ = 0x80000000
GENERIC_EXECUTE = 0x20000000
DELETE = 0x00010000
SYNCHRONIZE = 0x100000
STANDARD_RIGHTS_REQUIRED = 0xf0000
STANDARD_RIGHTS_READ = 0x20000
STANDARD_RIGHTS_WRITE = 0x20000
STANDARD_RIGHTS_EXECUTE = 0x20000
FILE_READ_DATA = 1
FILE_WRITE_DATA = 2
FILE_APPEND_DATA = 4
FILE_READ_EA = 8
FILE_WRITE_EA = 16
FILE_EXECUTE = 32
FILE_DELETE_CHILD = 64
FILE_READ_ATTRIBUTES = 128
FILE_WRITE_ATTRIBUTES = 256
FILE_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF
FILE_GENERIC_READ =
STANDARD_RIGHTS_READ |
FILE_READ_DATA |
FILE_READ_ATTRIBUTES |
FILE_READ_EA |
SYNCHRONIZE
FILE_GENERIC_WRITE =
STANDARD_RIGHTS_WRITE |
FILE_WRITE_DATA |
FILE_WRITE_ATTRIBUTES |
FILE_WRITE_EA |
FILE_APPEND_DATA |
SYNCHRONIZE
FILE_GENERIC_EXECUTE =
STANDARD_RIGHTS_EXECUTE |
FILE_READ_ATTRIBUTES |
FILE_EXECUTE |
SYNCHRONIZE
@security_descriptor = nil
# Puppet 3.7 deprecated methods at old locations in favor of SID class
def name_to_sid(name)
if Puppet::Util::Windows::SID.respond_to?(:name_to_sid)
Puppet::Util::Windows::SID.name_to_sid(name)
else
Puppet::Util::Windows::Security.name_to_sid(name)
end
end
def sid_to_name(value)
if Puppet::Util::Windows::SID.respond_to?(:sid_to_name)
Puppet::Util::Windows::SID.sid_to_name(value)
else
Puppet::Util::Windows::Security.sid_to_name(value)
end
end
def valid_sid?(string_sid)
if Puppet::Util::Windows::SID.respond_to?(:valid_sid?)
Puppet::Util::Windows::SID.valid_sid?(string_sid)
else
Puppet::Util::Windows::Security.valid_sid?(string_sid)
end
end
def get_current_permissions
sd = get_security_descriptor(DO_NOT_REFRESH_SD)
permissions = []
permissions if sd.nil? || sd.dacl.nil?
sd.dacl.each do |ace|
permissions << Puppet::Type::Acl::Ace.new(convert_to_permissions_hash(ace), self)
end
permissions
end
def convert_to_permissions_hash(ace)
return {} if ace.nil?
sid = ace.sid
identity = sid_to_name(sid)
rights = get_ace_rights_from_mask(ace)
ace_type = get_ace_type(ace)
child_types = get_ace_child_types(ace)
affects = get_ace_propagation(ace)
is_inherited = ace.inherited?
hash = {'identity'=>"#{identity}", 'id'=>"#{sid}", 'rights'=>rights,
'perm_type'=>ace_type, 'child_types'=> child_types,
'affects'=>affects, 'is_inherited'=>is_inherited,
'mask'=>"#{ace.mask}" }
hash
end
def get_ace_rights_from_mask(ace)
#todo v2 check that this is a file type and respond appropriately
rights = []
return rights if ace.nil?
mask_specific_remainder = ace.mask
# full
if (ace.mask & GENERIC_ALL) == GENERIC_ALL ||
(ace.mask & FILE_ALL_ACCESS) == FILE_ALL_ACCESS
rights << :full
mask_specific_remainder = 0
end
if rights == []
if (ace.mask & FILE_GENERIC_WRITE) == FILE_GENERIC_WRITE
rights << :write
mask_specific_remainder &= ~FILE_GENERIC_WRITE
end
if (ace.mask & GENERIC_WRITE) == GENERIC_WRITE
rights << :write
mask_specific_remainder &= ~GENERIC_WRITE
end
if (ace.mask & FILE_GENERIC_READ) == FILE_GENERIC_READ
rights << :read
mask_specific_remainder &= ~FILE_GENERIC_READ
end
if (ace.mask & GENERIC_READ) == GENERIC_READ
rights << :read
mask_specific_remainder &= ~GENERIC_READ
end
if (ace.mask & FILE_GENERIC_EXECUTE) == FILE_GENERIC_EXECUTE
rights << :execute
mask_specific_remainder &= ~FILE_GENERIC_EXECUTE
end
if (ace.mask & GENERIC_EXECUTE) == GENERIC_EXECUTE
rights << :execute
mask_specific_remainder &= ~GENERIC_EXECUTE
end
end
# modify
# if the rights appending changes above, we'll
# need to ensure this check is still good
if rights == [:write,:read,:execute] &&
(ace.mask & DELETE) == DELETE
rights = [:modify]
mask_specific_remainder &= ~DELETE
end
# rights are too specific, use mask
if rights == []
rights << :mask_specific
elsif mask_specific_remainder != 0
Puppet.debug("Remainder from #{ace.mask} is #{mask_specific_remainder}")
rights = [:mask_specific]
end
rights
end
module_function :get_ace_rights_from_mask
def get_ace_type(ace)
return :allow if ace.nil?
ace_type = case ace.type
when Puppet::Util::Windows::AccessControlEntry::ACCESS_ALLOWED_ACE_TYPE then :allow
when Puppet::Util::Windows::AccessControlEntry::ACCESS_DENIED_ACE_TYPE then :deny
end
ace_type
end
module_function :get_ace_type
def get_ace_child_types(ace)
return :all if ace.nil?
# the order is on purpose
child_types = :none
child_types = :objects if ace.object_inherit?
child_types = :containers if ace.container_inherit?
child_types = :all if ace.object_inherit? && ace.container_inherit?
child_types
end
module_function :get_ace_child_types
def get_ace_propagation(ace)
# http://msdn.microsoft.com/en-us/library/ms229747.aspx
return :all if ace.nil?
targets_self = true unless ace.inherit_only?
targets_children = true if ace.object_inherit? || ace.container_inherit?
targets_children_only = true if ace.inherit_only?
# the order is on purpose
affects = :self_only if targets_self
affects = :children_only if targets_children_only
affects = :all if targets_self && targets_children
# Puppet::Util::Windows::AccessControlEntry defines the propagation flag but doesn't provide a method
# http://msdn.microsoft.com/en-us/library/windows/desktop/ms692524(v=vs.85).aspx
no_propagate_flag = 0x4
propagate = ace.flags & no_propagate_flag != no_propagate_flag
unless propagate
affects = :self_and_direct_children_only if targets_self && targets_children
affects = :direct_children_only if targets_children_only
end
affects
end
module_function :get_ace_propagation
def are_permissions_insync?(current_permissions, specified_permissions, purge_value = :false)
return false if current_permissions.nil? && !specified_permissions.nil? && purge_value != :listed_permissions
purge_value = purge_value.to_s.downcase.to_sym unless purge_value.is_a?(Symbol)
should_purge = purge_value == :true
remove_permissions = purge_value == :listed_permissions
if current_permissions.nil?
current_local_permissions = []
else
current_local_permissions = current_permissions.select { |p| !p.is_inherited? }
end
if should_purge
current_local_permissions == specified_permissions
elsif remove_permissions
return true if specified_permissions.nil?
(specified_permissions & current_local_permissions) == []
else
return true if specified_permissions.nil?
# intersect will return order by left item in intersect
# order is guaranteed checked when specified_permissions
(current_local_permissions & specified_permissions) == specified_permissions
end
end
def convert_to_dacl(permissions)
dacl = Puppet::Util::Windows::AccessControlList.new
return dacl if permissions.nil? || permissions.empty?
permissions.each do |permission|
sid = get_account_id(permission.identity)
mask = get_account_mask(permission)
flags = get_account_flags(permission)
case permission.perm_type
when :allow
dacl.allow(sid, mask, flags)
when :deny
dacl.deny(sid, mask, flags)
end
end
dacl
end
def get_account_mask(permission, target_resource_type = :file)
return 0 if permission.nil?
return permission.mask.to_i if permission.mask
return 0 if permission.rights.nil? || permission.rights.empty?
mask = case target_resource_type
when :file
begin
if permission.rights.include?(:full)
return FILE_ALL_ACCESS
end
if permission.rights.include?(:modify)
return DELETE |
FILE_GENERIC_WRITE |
FILE_GENERIC_READ |
FILE_GENERIC_EXECUTE
end
filemask = 0x0
if permission.rights.include?(:write)
filemask = filemask | FILE_GENERIC_WRITE
end
if permission.rights.include?(:read)
filemask = filemask | FILE_GENERIC_READ
end
if permission.rights.include?(:execute)
filemask = filemask | FILE_GENERIC_EXECUTE
end
filemask
end
end
mask
end
module_function :get_account_mask
def get_account_flags(permission)
# http://msdn.microsoft.com/en-us/library/ms229747.aspx
flags = 0x0
case permission.child_types
when :all
flags = flags |
Puppet::Util::Windows::AccessControlEntry::OBJECT_INHERIT_ACE |
Puppet::Util::Windows::AccessControlEntry::CONTAINER_INHERIT_ACE
when :objects
flags = flags |
Puppet::Util::Windows::AccessControlEntry::OBJECT_INHERIT_ACE
when :containers
flags = flags |
Puppet::Util::Windows::AccessControlEntry::CONTAINER_INHERIT_ACE
end
case permission.affects
when :self_only
flags = 0x0
when :children_only
flags = flags |
Puppet::Util::Windows::AccessControlEntry::INHERIT_ONLY_ACE
when :self_and_direct_children_only
flags = flags |
Puppet::Util::Windows::AccessControlEntry::NO_PROPAGATE_INHERIT_ACE
when :direct_children_only
flags = flags |
Puppet::Util::Windows::AccessControlEntry::NO_PROPAGATE_INHERIT_ACE |
Puppet::Util::Windows::AccessControlEntry::INHERIT_ONLY_ACE
end
if (permission.child_types == :none && flags != 0x0)
flags = 0x0
end
flags
end
module_function :get_account_flags
def sync_aces(current_dacl, should_aces, should_purge = false, remove_permissions = false)
unless remove_permissions
return should_aces if should_purge
current_dacl.each do |ace|
# todo v2 should we warn if we have an existing inherited ace that matches?
next if ace.inherited?
current_ace = Puppet::Type::Acl::Ace.new(convert_to_permissions_hash(ace), self)
existing_aces = should_aces.select { |a| a.same?(current_ace) }
next unless existing_aces.empty?
# munge in existing unmanaged aces
case current_ace.perm_type
when :deny
last_allow_index = should_aces.index{ |a| a.perm_type == :allow}
should_aces.insert(last_allow_index,current_ace) if last_allow_index
should_aces << current_ace unless last_allow_index
when :allow
should_aces << current_ace
end
end
else
kept_aces = []
current_dacl.each do |ace|
next if ace.inherited?
current_ace = Puppet::Type::Acl::Ace.new(convert_to_permissions_hash(ace), self)
existing_aces = should_aces.select { |a| a.same?(current_ace) }
next unless existing_aces.empty?
kept_aces << current_ace
end
should_aces = kept_aces
end
should_aces
end
def get_current_owner
sd = get_security_descriptor
sd.owner unless sd.nil?
end
def get_current_group
sd = get_security_descriptor
sd.group unless sd.nil?
end
def is_account_insync?(current, should)
return false unless current
should_empty = should.nil? || should.empty?
return false if current.empty? != should_empty
get_account_id(current) == get_account_id(should)
end
def get_account_id(name)
# sometimes the name will come in with a SID
# which will return nil when we call name_to_sid
# if the user no longer exists
if valid_sid?(name)
name
else
name_to_sid(name)
end
end
def get_account_name(current_value)
name = sid_to_name(get_account_id(current_value))
name ? name : current_value
end
alias_method :get_group_name, :get_account_name
def is_inheriting_permissions?
sd = get_security_descriptor
return !sd.protect unless sd.nil?
# default true
true
end
def get_security_descriptor(refresh_sd = DO_NOT_REFRESH_SD)
refresh_sd ||= false
if @security_descriptor.nil? || refresh_sd
sd = nil
case @resource[:target_type]
when :file
begin
sd = Puppet::Util::Windows::Security.get_security_descriptor(@resource[:target])
rescue => detail
raise Puppet::Error, "Failed to get security descriptor for path '#{@resource[:target]}': #{detail}", detail.backtrace
end
end
@security_descriptor = sd
end
@security_descriptor
end
def set_security_descriptor(security_descriptor)
case @resource[:target_type]
when :file
begin
Puppet::Util::Windows::Security.set_security_descriptor(@resource[:target], security_descriptor)
rescue => detail
raise Puppet::Error, "Failed to set security descriptor for path '#{@resource[:target]}': #{detail}", detail.backtrace
end
end
# flush out the cached sd
get_security_descriptor(REFRESH_SD)
end
end
end
end
end
#todo v2 legacy - check to see if method exists for Puppet::Util::Windows::Security.get_security_descriptor, if not - we'll need to create it
--