Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BREAKING: Add selinux_fcontext and selinux_fcontext_equivalence types #177

Merged
merged 9 commits into from
Jan 31, 2017
120 changes: 120 additions & 0 deletions lib/puppet/provider/selinux_fcontext/semanage.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
Puppet::Type.type(:selinux_fcontext).provide(:semanage) do
desc 'Support managing SELinux custom fcontext definitions via semanage'

defaultfor kernel: 'Linux'

commands semanage: 'semanage'
# semanage fails when SELinux is disabled, so let's not pretend to work in that situation.
confine selinux: true

mk_resource_methods

osfamily = Facter.value('osfamily')
osversion = Facter.value('operatingsystemmajrelease')
@old_semanage = false
if (osfamily == 'RedHat') && (Puppet::Util::Package.versioncmp(osversion, '6') <= 0)
@old_semanage = true
end

@file_types = {
'all files' => 'a',
'-d' => 'd',
'-c' => 'c',
'-b' => 'b',
'-l' => 'l',
'-p' => 'p',
'--' => 'f',
'-s' => 's'
}

def self.file_type_map(val)
@file_types[val]
end

def self.type_param(file_type)
return file_type unless @old_semanage
@file_types.invert[file_type]
end

def self.parse_fcontext_lines(lines)
ret = []
lines.each do |line|
next if line.strip.empty?
next if line =~ %r{^#}
split = line.split(%r{\s+})
if split.length == 2
path_spec, context_spec = split
file_type = 'all files'
else
path_spec, file_type, context_spec = split
end
user, role, type, range = context_spec.split(':')
if context_spec == '<<none>>'
type = '<<none>>'
user = range = role = nil
end
ft = file_type_map(file_type)
ret.push(new(ensure: :present,
name: "#{path_spec}_#{ft}",
pathspec: path_spec,
seltype: type,
seluser: user,
selrole: role,
selrange: range,
file_type: ft))
end
ret
end

def self.instances
# With fcontext, we only need to care about local customisations as they
# should never conflict with system policy
# Old semanage fails with --locallist, use -C
local_fcs = Selinux.selinux_file_context_local_path
if File.exist? local_fcs
parse_fcontext_lines(File.readlines(local_fcs))
else
# no file, no local contexts
[]
end
end

def self.prefetch(resources)
# is there a better way to do this? map port/protocol pairs to the provider regardless of the title
# and make sure all system resources have ensure => :present so that we don't try to remove them
instances.each do |provider|
resource = resources[provider.name]
resource.provider = provider if resource
end
end

def create
# is there really no way to have a provider-global helper function cleanly?
args = ['fcontext', '-a', '-t', @resource[:seltype], '-f', self.class.type_param(@resource[:file_type])]
args.concat(['-s', @resource[:seluser]]) if @resource[:seluser]
args.push(@resource[:pathspec])
semanage(*args)
end

def destroy
args = ['fcontext', '-d', '-t', @property_hash[:seltype], '-f', self.class.type_param(@property_hash[:file_type])]
args.concat(['-s', @property_hash[:seluser]]) if @property_hash[:seluser]
args.push(@property_hash[:pathspec])
semanage(*args)
end

def seltype=(val)
val = '<<none>>' if val == :none
args = ['fcontext', '-m', '-t', val, '-f', self.class.type_param(@property_hash[:file_type]), @property_hash[:pathspec]]
semanage(*args)
end

def seluser=(val)
args = ['fcontext', '-m', '-s', val, '-t', @property_hash[:seltype], '-f', self.class.type_param(@property_hash[:file_type]), @property_hash[:pathspec]]
semanage(*args)
end

def exists?
@property_hash[:ensure] == :present
end
end
63 changes: 63 additions & 0 deletions lib/puppet/provider/selinux_fcontext_equivalence/semanage.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
Puppet::Type.type(:selinux_fcontext_equivalence).provide(:semanage) do
desc 'Support managing SELinux custom fcontext definitions via semanage'

defaultfor kernel: 'Linux'

commands semanage: 'semanage'
confine selinux: true

mk_resource_methods

def self.parse_fcontext_subs_lines(lines)
ret = []
lines.each do |line|
next if line.strip.empty?
next if line =~ %r{^#}
source, target = line.split(%r{\s+})
ret.push(new(ensure: :present,
name: source,
target: target))
end
ret
end

def self.instances
# Allow this to fail with an exception if it does not exist
path = Selinux.selinux_file_context_subs_path
if File.exist? path
lines = File.readlines(path)
parse_fcontext_subs_lines(lines)
else
# No file, no equivalences:
[]
end
end

def self.prefetch(resources)
# is there a better way to do this? map port/protocol pairs to the provider regardless of the title
# and make sure all system resources have ensure => :present so that we don't try to remove them
instances.each do |provider|
resource = resources[provider.name]
resource.provider = provider if resource
end
end

def create
semanage('fcontext', '-a', '-e', @resource[:target], @resource[:path])
end

def destroy
semanage('fcontext', '-d', '-e', @property_hash[:target], @property_hash[:name])
end

def target=(val)
# apparently --modify does not work... Must delete and create anew
# -N for noreload, so it's "atomic"
semanage('fcontext', '-N', '-d', '-e', @property_hash[:target], @property_hash[:name])
semanage('fcontext', '-a', '-e', val, @property_hash[:name])
end

def exists?
@property_hash[:ensure] == :present
end
end
47 changes: 47 additions & 0 deletions lib/puppet/type/selinux_fcontext.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
Puppet::Type.newtype(:selinux_fcontext) do
@doc = 'Manage SELinux fcontext definitions. You should use selinux::fcontext instead of this directly.'

ensurable

# The pathspec can't be the namevar because it is completely valid to have
# two with the same spec but different file type
newparam(:title, namevar: true) do
desc 'The namevar. Should be of the format pathspec_filetype'
end

newparam(:pathspec) do
desc 'Path regular expression'
isrequired
end

newproperty(:file_type) do
desc 'The file type to match'
# See semanage manual page.
newvalues(%r{^[abcdflps]$})
defaultto 'a'
isrequired
end

newproperty(:seltype) do
desc 'The SELinux type to apply to the paths'
isrequired
# <<none>> is the special value when the type is explicitly unset
newvalues(%r{\w+}, '<<none>>')
end

newproperty(:seluser) do
desc 'The SELinux user name'
newvalues(%r{\w+})
end

# These can't actually be set via fcontext
newproperty(:selrole) do
desc 'The SELinux role'
newvalues(%r{\w+})
end

newproperty(:selrange) do
desc 'The SELinux range'
newvalues(%r{\w+})
end
end
28 changes: 28 additions & 0 deletions lib/puppet/type/selinux_fcontext_equivalence.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
require 'pathname'

Puppet::Type.newtype(:selinux_fcontext_equivalence) do
@doc = 'Manage SELinux fcontext equivalence definitions. You should use selinux::fcontext instead of this directly.'

ensurable

# The pathspec can't be the namevar because it is completely valid to have
# two with the same spec but different file type
newparam(:path, namevar: true) do
desc 'The path to set equivalence for'
validate do |value|
unless Pathname.new(value).absolute?
raise ArgumentError, "An fcontext equivalence must specify an absolute path instead of '#{value}'"
end
end
end

newproperty(:target) do
desc 'The target of the equivalence. ie. the path that this resource will be equivalent to'
isrequired
validate do |value|
unless Pathname.new(value).absolute?
raise ArgumentError, "The fcontext equivalence target must be an absolute path instead of '#{value}'"
end
end
end
end
Loading