diff --git a/lib/puppet/type/file/selcontext.rb b/lib/puppet/type/file/selcontext.rb index 8571d9ad2e..ed0f0f8ea2 100644 --- a/lib/puppet/type/file/selcontext.rb +++ b/lib/puppet/type/file/selcontext.rb @@ -27,6 +27,10 @@ module Puppet class SELFileContext < Puppet::Property include Puppet::Util::SELinux + def skip_property? + !Puppet::Util::SELinux.selinux_support? || @resource[:selinux_ignore_defaults] == :true + end + def retrieve return :absent unless @resource.stat @@ -40,11 +44,6 @@ def retrieve end def retrieve_default_context(property) - return nil if Puppet::Util::Platform.windows? - if @resource[:selinux_ignore_defaults] == :true - return nil - end - context = get_selinux_default_context_with_handle(@resource[:path], provider.class.selinux_handle, @resource[:ensure]) unless context return nil @@ -63,10 +62,21 @@ def insync?(value) debug("SELinux not available for this filesystem. Ignoring parameter.") true else + @should = @should.collect { |v| v == :lookup ? retrieve_default_context(name) : v } + if @should.include?(nil) + return true + end + super end end + def validate(value) + unless value.is_a?(String) || value == :lookup + raise Puppet::Error, "The property #{name} must be either a string or :lookup" + end + end + def unsafe_munge(should) unless selinux_support? return should @@ -104,7 +114,7 @@ def sync enabled." @event = :file_changed - defaultto { retrieve_default_context(:seluser) } + defaultto { skip_property? ? nil : :lookup } end Puppet::Type.type(:file).newproperty(:selrole, :parent => Puppet::SELFileContext) do @@ -115,7 +125,7 @@ def sync enabled." @event = :file_changed - defaultto { retrieve_default_context(:selrole) } + defaultto { skip_property? ? nil : :lookup } end Puppet::Type.type(:file).newproperty(:seltype, :parent => Puppet::SELFileContext) do @@ -126,7 +136,7 @@ def sync enabled." @event = :file_changed - defaultto { retrieve_default_context(:seltype) } + defaultto { skip_property? ? nil : :lookup } end Puppet::Type.type(:file).newproperty(:selrange, :parent => Puppet::SELFileContext) do @@ -138,6 +148,6 @@ def sync Security)." @event = :file_changed - defaultto { retrieve_default_context(:selrange) } + defaultto { skip_property? ? nil : :lookup } end end diff --git a/lib/puppet/util/selinux.rb b/lib/puppet/util/selinux.rb index a73597ea65..303a81f962 100644 --- a/lib/puppet/util/selinux.rb +++ b/lib/puppet/util/selinux.rb @@ -188,6 +188,10 @@ def selinux_category_to_label(category) # We don't cache this, but there's already a ton of duplicate work # in the selinux handling code. + if category == :lookup + return category + end + path = Selinux.selinux_translations_path begin File.open(path).each do |line| diff --git a/spec/unit/resource/catalog_spec.rb b/spec/unit/resource/catalog_spec.rb index 7abca7e7b5..ddccab3e4d 100644 --- a/spec/unit/resource/catalog_spec.rb +++ b/spec/unit/resource/catalog_spec.rb @@ -21,6 +21,11 @@ # audit only resources are unmanaged # as are resources without properties with should values it "should write its managed resources' types, namevars" do + if Puppet.features.selinux? + selinux = class_double('selinux', is_selinux_enabled: 0) + stub_const('Selinux', selinux) + end + catalog = Puppet::Resource::Catalog.new("host") resourcefile = tmpfile('resourcefile') diff --git a/spec/unit/transaction/resource_harness_spec.rb b/spec/unit/transaction/resource_harness_spec.rb index 4285b3e377..e19d8ce299 100644 --- a/spec/unit/transaction/resource_harness_spec.rb +++ b/spec/unit/transaction/resource_harness_spec.rb @@ -29,6 +29,11 @@ describe "when evaluating a resource" do it "produces a resource state that describes what happened with the resource" do + if Puppet.features.selinux? + selinux = class_double('selinux', is_selinux_enabled: 0) + stub_const('Selinux', selinux) + end + status = @harness.evaluate(@resource) expect(status.resource).to eq(@resource.ref) diff --git a/spec/unit/transaction_spec.rb b/spec/unit/transaction_spec.rb index 716d5d0762..14cb61bf5e 100644 --- a/spec/unit/transaction_spec.rb +++ b/spec/unit/transaction_spec.rb @@ -760,14 +760,26 @@ def post_resource_eval it "should call Selinux.selabel_close in case Selinux is enabled", :if => Puppet.features.posix? do handle = double('selinux_handle') - selinux = class_double('selinux', is_selinux_enabled: 1, selabel_close: nil, selabel_open: handle, selabel_lookup: -1) - stub_const('Selinux', selinux) - stub_const('Selinux::SELABEL_CTX_FILE', 0) - resource = Puppet::Type.type(:file).new(:path => make_absolute("/tmp/foo")) + selinux = class_double('selinux', + is_selinux_enabled: 1, + lgetfilecon: [nil, "a:b:c:d"], + selabel_open: handle, + selabel_lookup: -1, + selinux_translations_path: "/dev/null", + selabel_close: nil, + ) + + if Puppet.features.selinux? + stub_const('Selinux', selinux, :transfer_nested_constants => [:SELABEL_CTX_FILE]) + else + stub_const('Selinux', selinux) + stub_const('Selinux::SELABEL_CTX_FILE', 0) + end + + resource = Puppet::Type.type(:file).new(:path => make_absolute("/tmp/foo"), :ensure => :file) transaction = transaction_with_resource(resource) expect(Selinux).to receive(:selabel_close).with(handle) - transaction.evaluate end end diff --git a/spec/unit/type/file/selinux_spec.rb b/spec/unit/type/file/selinux_spec.rb index 11a4878f6f..7afc5f2375 100644 --- a/spec/unit/type/file/selinux_spec.rb +++ b/spec/unit/type/file/selinux_spec.rb @@ -49,34 +49,41 @@ expect(@sel.retrieve).to eq(expectedresult) end - it "should handle no default gracefully" do - skip if Puppet::Util::Platform.windows? - expect(@sel).to receive(:get_selinux_default_context_with_handle).with(@path, nil, :file).and_return(nil) + it "has a default value of :lookup if SELinux is supported" do + allow(Puppet::Util::SELinux).to receive(:selinux_support?).and_return(true) + expect(@sel.default).to eq(:lookup) + end + + it "has a default value of nil if SELinux is not supported" do + allow(Puppet::Util::SELinux).to receive(:selinux_support?).and_return(false) expect(@sel.default).to be_nil end - it "should be able to detect default context on platforms other than Windows", unless: Puppet::Util::Platform.windows? do - allow(@sel).to receive(:debug) + it "has a default value of nil if selinux_ignore_defaults is true" do + @resource[:selinux_ignore_defaults] = :true + expect(@sel.default).to be_nil + end + + it "looks up the default context when checking sync status", unless: Puppet::Util::Platform.windows? do + allow(@sel).to receive(:selinux_support?).and_return(true) + allow(@sel).to receive(:selinux_label_support?).with(@path).and_return(true) + + if param == :selrange + expect(@sel).to receive(:selinux_category_to_label).with(:lookup).and_return(:lookup) + end + @sel.should = [:lookup] + hnd = double("SWIG::TYPE_p_selabel_handle") allow(@sel.provider.class).to receive(:selinux_handle).and_return(hnd) expect(@sel).to receive(:get_selinux_default_context_with_handle).with(@path, hnd, :file).and_return("user_u:role_r:type_t:s0") - expectedresult = case param + + currentval = case param when :seluser; "user_u" when :selrole; "role_r" when :seltype; "type_t" when :selrange; "s0" end - expect(@sel.default).to eq(expectedresult) - end - - it "returns nil default context on Windows", if: Puppet::Util::Platform.windows? do - expect(@sel).to receive(:retrieve_default_context) - expect(@sel.default).to be_nil - end - - it "should return nil for defaults if selinux_ignore_defaults is true" do - @resource[:selinux_ignore_defaults] = :true - expect(@sel.default).to be_nil + expect(@sel.insync?(currentval)).to be(true) end it "should be able to set a new context" do