module Chef::FileAccessControl::Windows

Constants

ACE
ACL
SID
Security

Public Class Methods

included(base) click to toggle source
# File lib/chef/file_access_control/windows.rb, line 42
def self.included(base)
  # When this file is mixed in, make sure we also add the class methods
  base.send :extend, ClassMethods
end

Public Instance Methods

define_resource_requirements() click to toggle source
# File lib/chef/file_access_control/windows.rb, line 59
def define_resource_requirements
  # windows FAC has no assertions
end
describe_changes() click to toggle source
# File lib/chef/file_access_control/windows.rb, line 67
def describe_changes
  # FIXME: describe what these are changing from and to
  changes = []
  changes << "change dacl" if should_update_dacl?
  changes << "change owner" if should_update_owner?
  changes << "change group" if should_update_group?
  changes
end
requires_changes?() click to toggle source
# File lib/chef/file_access_control/windows.rb, line 63
def requires_changes?
  should_update_dacl? || should_update_owner? || should_update_group?
end
set_all() click to toggle source
# File lib/chef/file_access_control/windows.rb, line 53
def set_all
  set_owner
  set_group
  set_dacl
end
set_all!() click to toggle source
# File lib/chef/file_access_control/windows.rb, line 47
def set_all!
  set_owner!
  set_group!
  set_dacl
end

Private Instance Methods

acls_equal(target_acl, actual_acl) click to toggle source

Compare the actual ACL on a resource with the ACL we want. This ignores explicit ACLs on the target, and does mask prediction (if you set GENERIC_WRITE, Windows will flip on a whole bunch of other rights on the file when you save the ACL)

# File lib/chef/file_access_control/windows.rb, line 82
def acls_equal(target_acl, actual_acl)
  if actual_acl.nil?
    return target_acl.nil?
  end

  actual_acl = actual_acl.select { |ace| !ace.inherited? }
  # When ACLs apply to children, Windows splits them on the file system into two ACLs:
  # one specific applying to this container, and one generic applying to children.
  new_target_acl = []
  target_acl.each do |target_ace|
    if target_ace.flags & INHERIT_ONLY_ACE == 0
      self_ace = target_ace.dup
      # We need flag value which is already being set in case of WRITE permissions as 3, so we will not be overwriting it with the hard coded value.
      self_ace.flags = 0 unless target_ace.mask == Chef::ReservedNames::Win32::API::Security::WRITE
      self_ace.mask = securable_object.predict_rights_mask(target_ace.mask)
      new_target_acl << self_ace
    end
    # As there is no inheritance needed in case of WRITE permissions.
    if target_ace.mask != Chef::ReservedNames::Win32::API::Security::WRITE && target_ace.flags & (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE) != 0
      children_ace = target_ace.dup
      children_ace.flags |= INHERIT_ONLY_ACE
      new_target_acl << children_ace
    end
  end
  actual_acl == new_target_acl
end
calculate_flags(rights) click to toggle source
# File lib/chef/file_access_control/windows.rb, line 244
def calculate_flags(rights)
  # Handle inheritance flags
  flags = 0

  #
  # Configure child inheritance only if the resource is some
  # type of a directory.
  #
  if resource.is_a? Chef::Resource::Directory
    case rights[:applies_to_children]
    when :containers_only
      flags |= CONTAINER_INHERIT_ACE
    when :objects_only
      flags |= OBJECT_INHERIT_ACE
    when true, nil
      flags |= CONTAINER_INHERIT_ACE
      flags |= OBJECT_INHERIT_ACE
    end
  end

  if rights[:applies_to_self] == false
    flags |= INHERIT_ONLY_ACE
  end

  if rights[:one_level_deep]
    flags |= NO_PROPAGATE_INHERIT_ACE
  end
  flags
end
calculate_mask(permissions) click to toggle source
# File lib/chef/file_access_control/windows.rb, line 222
def calculate_mask(permissions)
  mask = 0
  [ permissions ].flatten.each do |permission|
    case permission
    when :full_control
      mask |= GENERIC_ALL
    when :modify
      mask |= GENERIC_WRITE | GENERIC_READ | GENERIC_EXECUTE | DELETE
    when :read
      mask |= GENERIC_READ
    when :read_execute
      mask |= GENERIC_READ | GENERIC_EXECUTE
    when :write
      mask |= WRITE
    else
      # Otherwise, assume it's an integer specifying the actual flags
      mask |= permission
    end
  end
  mask
end
existing_descriptor() click to toggle source
# File lib/chef/file_access_control/windows.rb, line 109
def existing_descriptor
  securable_object.security_descriptor
end
get_sid(value) click to toggle source
# File lib/chef/file_access_control/windows.rb, line 113
def get_sid(value)
  if value.is_a?(String)
    begin
      Security.convert_string_sid_to_sid(value)
    rescue Chef::Exceptions::Win32APIError
      SID.from_account(value)
    end
  elsif value.is_a?(SID)
    value
  else
    raise "Must specify username, group or SID: #{value}"
  end
end
mode_ace(sid, mode) click to toggle source
# File lib/chef/file_access_control/windows.rb, line 212
def mode_ace(sid, mode)
  mask = 0
  mask |= GENERIC_READ if mode & 4 != 0
  mask |= (GENERIC_WRITE | DELETE) if mode & 2 != 0
  mask |= GENERIC_EXECUTE if mode & 1 != 0
  return [] if mask == 0

  [ ACE.access_allowed(sid, mask) ]
end
securable_object() click to toggle source
# File lib/chef/file_access_control/windows.rb, line 127
def securable_object
  @securable_object ||= begin
    if file.is_a?(String)
      so = Chef::ReservedNames::Win32::Security::SecurableObject.new(file.dup)
    end
    raise ArgumentError, "'file' must be a valid path or object of type 'Chef::ReservedNames::Win32::Security::SecurableObject'" unless so.is_a? Chef::ReservedNames::Win32::Security::SecurableObject

    so
  end
end
set_dacl() click to toggle source
# File lib/chef/file_access_control/windows.rb, line 151
def set_dacl
  dacl = target_dacl
  existing_dacl = existing_descriptor.dacl
  inherits = target_inherits
  if ! inherits.nil? && inherits != existing_descriptor.dacl_inherits?
    # We have to set DACL along with inherits.  If rights were not
    # specified, we need to change only inherited ACLs and leave
    # explicit ACLs alone.
    if dacl.nil? && !existing_dacl.nil?
      dacl = ACL.create(existing_dacl.select { |ace| !ace.inherited? })
    end
    securable_object.set_dacl(dacl, inherits)
    Chef::Log.info("#{log_string} permissions changed to #{dacl} with inherits of #{inherits}")
    modified
  elsif dacl && !acls_equal(dacl, existing_dacl)
    securable_object.dacl = dacl
    Chef::Log.info("#{log_string} permissions changed to #{dacl}")
    modified
  end
end
set_dacl!() click to toggle source
# File lib/chef/file_access_control/windows.rb, line 147
def set_dacl!
  set_dacl
end
set_group() click to toggle source
# File lib/chef/file_access_control/windows.rb, line 186
def set_group
  if (group = target_group) && (group != existing_descriptor.group)
    set_group!
  end
end
set_group!() click to toggle source
# File lib/chef/file_access_control/windows.rb, line 178
def set_group!
  if (group = target_group)
    Chef::Log.info("#{log_string} group changed to #{group}")
    securable_object.group = group
    modified
  end
end
set_owner() click to toggle source
# File lib/chef/file_access_control/windows.rb, line 206
def set_owner
  if (owner = target_owner) && (owner != existing_descriptor.owner)
    set_owner!
  end
end
set_owner!() click to toggle source
# File lib/chef/file_access_control/windows.rb, line 198
def set_owner!
  if owner = target_owner
    Chef::Log.info("#{log_string} owner changed to #{owner}")
    securable_object.owner = owner
    modified
  end
end
should_update_dacl?() click to toggle source
# File lib/chef/file_access_control/windows.rb, line 138
def should_update_dacl?
  return true unless ::File.exist?(file) || ::File.symlink?(file)

  dacl = target_dacl
  existing_dacl = existing_descriptor.dacl
  inherits = target_inherits
  ( ! inherits.nil? && inherits != existing_descriptor.dacl_inherits? ) || ( dacl && !acls_equal(dacl, existing_dacl) )
end
should_update_group?() click to toggle source
# File lib/chef/file_access_control/windows.rb, line 172
def should_update_group?
  return true unless ::File.exist?(file) || ::File.symlink?(file)

  (group = target_group) && (group != existing_descriptor.group)
end
should_update_owner?() click to toggle source
# File lib/chef/file_access_control/windows.rb, line 192
def should_update_owner?
  return true unless ::File.exist?(file) || ::File.symlink?(file)

  (owner = target_owner) && (owner != existing_descriptor.owner)
end
target_dacl() click to toggle source
# File lib/chef/file_access_control/windows.rb, line 274
def target_dacl
  return nil if resource.rights.nil? && resource.deny_rights.nil? && resource.mode.nil?

  acls = nil

  unless resource.deny_rights.nil?
    acls = [] if acls.nil?

    resource.deny_rights.each do |rights|
      mask = calculate_mask(rights[:permissions])
      [ rights[:principals] ].flatten.each do |principal|
        sid = get_sid(principal)
        flags = calculate_flags(rights)
        acls.push ACE.access_denied(sid, mask, flags)
      end
    end
  end

  unless resource.rights.nil?
    acls = [] if acls.nil?

    resource.rights.each do |rights|
      mask = calculate_mask(rights[:permissions])
      [ rights[:principals] ].flatten.each do |principal|
        sid = get_sid(principal)
        flags = calculate_flags(rights)
        acls.push ACE.access_allowed(sid, mask, flags)
      end
    end
  end

  unless resource.mode.nil?
    acls = [] if acls.nil?

    mode = (resource.mode.respond_to?(:oct) ? resource.mode.oct : resource.mode.to_i) & 0777

    owner = target_owner
    if owner
      acls += mode_ace(owner, (mode & 0700) >> 6)
    elsif mode & 0700 != 0
      Chef::Log.warn("Mode #{sprintf("%03o", mode)} includes bits for the owner, but owner is not specified")
    end

    group = target_group
    if group
      acls += mode_ace(group, (mode & 070) >> 3)
    elsif mode & 070 != 0
      Chef::Log.warn("Mode #{sprintf("%03o", mode)} includes bits for the group, but group is not specified")
    end

    acls += mode_ace(SID.Everyone, (mode & 07))
  end

  acls.nil? ? nil : Chef::ReservedNames::Win32::Security::ACL.create(acls)
end
target_group() click to toggle source
# File lib/chef/file_access_control/windows.rb, line 330
def target_group
  return nil if resource.group.nil?

  get_sid(resource.group)
end
target_inherits() click to toggle source
# File lib/chef/file_access_control/windows.rb, line 336
def target_inherits
  resource.inherits
end
target_owner() click to toggle source
# File lib/chef/file_access_control/windows.rb, line 340
def target_owner
  return nil if resource.owner.nil?

  get_sid(resource.owner)
end