From 976f0ab38a6ff514d5ee1513d551b6c16074ac70 Mon Sep 17 00:00:00 2001 From: Aria Li Date: Thu, 20 Jun 2024 11:56:46 -0700 Subject: [PATCH] Revert "Merge pull request #9390 from AriaXLi/revert_selinux_matchpathcon" This reverts commit 2417766ced648f6fc67972a114fa2a06b573483f, reversing changes made to 7927dcf53cad403625b1c862000296833597c9d0. --- lib/puppet/provider/file/posix.rb | 18 ++++++++++++++++-- lib/puppet/type/file/selcontext.rb | 13 +++++++------ lib/puppet/util/selinux.rb | 18 ++++++++++++++---- spec/unit/transaction_spec.rb | 10 +++++----- spec/unit/type/file/selinux_spec.rb | 14 +++++++++++--- spec/unit/util/selinux_spec.rb | 29 +++++++++++++++++++++++++++++ 6 files changed, 82 insertions(+), 20 deletions(-) diff --git a/lib/puppet/provider/file/posix.rb b/lib/puppet/provider/file/posix.rb index 445dbb13cbf..f4c645b6336 100644 --- a/lib/puppet/provider/file/posix.rb +++ b/lib/puppet/provider/file/posix.rb @@ -12,8 +12,22 @@ require 'etc' require_relative '../../../puppet/util/selinux' - def self.post_resource_eval - Selinux.matchpathcon_fini if Puppet::Util::SELinux.selinux_support? + class << self + def selinux_handle + return nil unless Puppet::Util::SELinux.selinux_support? + + # selabel_open takes 3 args: backend, options, and nopt. The backend param + # is a constant, SELABEL_CTX_FILE, which happens to be 0. Since options is + # nil, nopt can be 0 since nopt represents the # of options specified. + @selinux_handle ||= Selinux.selabel_open(Selinux::SELABEL_CTX_FILE, nil, 0) + end + + def post_resource_eval + if @selinux_handle + Selinux.selabel_close(@selinux_handle) + @selinux_handle = nil + end + end end def uid2name(id) diff --git a/lib/puppet/type/file/selcontext.rb b/lib/puppet/type/file/selcontext.rb index 225bb5f2c5e..474c64a0ccc 100644 --- a/lib/puppet/type/file/selcontext.rb +++ b/lib/puppet/type/file/selcontext.rb @@ -40,11 +40,12 @@ 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(@resource[:path], @resource[:ensure]) + context = get_selinux_default_context_with_handle(@resource[:path], provider.class.selinux_handle) unless context return nil end @@ -85,7 +86,7 @@ def sync end Puppet::Type.type(:file).newparam(:selinux_ignore_defaults) do - desc "If this is set then Puppet will not ask SELinux (via matchpathcon) to + desc "If this is set then Puppet will not ask SELinux (via selabel_lookup) to supply defaults for the SELinux attributes (seluser, selrole, seltype, and selrange). In general, you should leave this set at its default and only set it to true when you need Puppet to not try to fix @@ -98,7 +99,7 @@ def sync Puppet::Type.type(:file).newproperty(:seluser, :parent => Puppet::SELFileContext) do desc "What the SELinux user component of the context of the file should be. Any valid SELinux user component is accepted. For example `user_u`. - If not specified it defaults to the value returned by matchpathcon for + If not specified it defaults to the value returned by selabel_lookup for the file, if any exists. Only valid on systems with SELinux support enabled." @@ -109,7 +110,7 @@ def sync Puppet::Type.type(:file).newproperty(:selrole, :parent => Puppet::SELFileContext) do desc "What the SELinux role component of the context of the file should be. Any valid SELinux role component is accepted. For example `role_r`. - If not specified it defaults to the value returned by matchpathcon for + If not specified it defaults to the value returned by selabel_lookup for the file, if any exists. Only valid on systems with SELinux support enabled." @@ -120,7 +121,7 @@ def sync Puppet::Type.type(:file).newproperty(:seltype, :parent => Puppet::SELFileContext) do desc "What the SELinux type component of the context of the file should be. Any valid SELinux type component is accepted. For example `tmp_t`. - If not specified it defaults to the value returned by matchpathcon for + If not specified it defaults to the value returned by selabel_lookup for the file, if any exists. Only valid on systems with SELinux support enabled." @@ -132,7 +133,7 @@ def sync desc "What the SELinux range component of the context of the file should be. Any valid SELinux range component is accepted. For example `s0` or `SystemHigh`. If not specified it defaults to the value returned by - matchpathcon for the file, if any exists. Only valid on systems with + selabel_lookup for the file, if any exists. Only valid on systems with SELinux support enabled and that have support for MCS (Multi-Category Security)." diff --git a/lib/puppet/util/selinux.rb b/lib/puppet/util/selinux.rb index cd2c636f2e7..bfb730ecc95 100644 --- a/lib/puppet/util/selinux.rb +++ b/lib/puppet/util/selinux.rb @@ -46,6 +46,7 @@ def get_selinux_current_context(file) # Retrieve and return the default context of the file. If we don't have # SELinux support or if the SELinux call fails to file a default then return nil. + # @deprecated matchpathcon is a deprecated method, selabel_lookup is preferred def get_selinux_default_context(file, resource_ensure = nil) return nil unless selinux_support? # If the filesystem has no support for SELinux labels, return a default of nil @@ -68,11 +69,20 @@ def get_selinux_default_context(file, resource_ensure = nil) end retval = Selinux.matchpathcon(file, mode) - if retval == -1 - return nil - end + retval == -1 ? nil : retval[1] + end - retval[1] + def get_selinux_default_context_with_handle(file, handle) + return nil unless selinux_support? + # If the filesystem has no support for SELinux labels, return a default of nil + # instead of what selabel_lookup would return + return nil unless selinux_label_support?(file) + + # Handle is needed for selabel_lookup + raise ArgumentError, _("Cannot get default context with nil handle") unless handle + + retval = Selinux.selabel_lookup(handle, file, 0) + retval == -1 ? nil : retval[1] end # Take the full SELinux context returned from the tools and parse it diff --git a/spec/unit/transaction_spec.rb b/spec/unit/transaction_spec.rb index c7c278bf1c7..716d5d07625 100644 --- a/spec/unit/transaction_spec.rb +++ b/spec/unit/transaction_spec.rb @@ -758,15 +758,15 @@ def post_resource_eval transaction.evaluate end - it "should call Selinux.matchpathcon_fini in case Selinux is enabled ", :if => Puppet.features.posix? do - selinux = double('selinux', is_selinux_enabled: true, matchpathcon_fini: nil) + 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")) transaction = transaction_with_resource(resource) - expect(Selinux).to receive(:matchpathcon_fini) - expect(Puppet::Util::SELinux).to receive(:selinux_support?).and_return(true) + expect(Selinux).to receive(:selabel_close).with(handle) transaction.evaluate end diff --git a/spec/unit/type/file/selinux_spec.rb b/spec/unit/type/file/selinux_spec.rb index 2292bfd3496..aeedefacebe 100644 --- a/spec/unit/type/file/selinux_spec.rb +++ b/spec/unit/type/file/selinux_spec.rb @@ -50,13 +50,16 @@ end it "should handle no default gracefully" do - expect(@sel).to receive(:get_selinux_default_context).with(@path, :file).and_return(nil) + skip if Puppet::Util::Platform.windows? + expect(@sel).to receive(:get_selinux_default_context_with_handle).with(@path, nil).and_return(nil) expect(@sel.default).to be_nil end - it "should be able to detect matchpathcon defaults" do + it "should be able to detect default context on platforms other than Windows", unless: Puppet::Util::Platform.windows? do allow(@sel).to receive(:debug) - expect(@sel).to receive(:get_selinux_default_context).with(@path, :file).and_return("user_u:role_r:type_t:s0") + 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).and_return("user_u:role_r:type_t:s0") expectedresult = case param when :seluser; "user_u" when :selrole; "role_r" @@ -66,6 +69,11 @@ 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 diff --git a/spec/unit/util/selinux_spec.rb b/spec/unit/util/selinux_spec.rb index 21c1fea4801..1df1c46b785 100644 --- a/spec/unit/util/selinux_spec.rb +++ b/spec/unit/util/selinux_spec.rb @@ -241,6 +241,35 @@ end end + describe "get_selinux_default_context_with_handle" do + it "should return a context if a default context exists" do + without_partial_double_verification do + expect(self).to receive(:selinux_support?).and_return(true) + expect(self).to receive(:find_fs).with("/foo").and_return("ext3") + hnd = double("SWIG::TYPE_p_selabel_handle") + expect(Selinux).to receive(:selabel_lookup).with(hnd, '/foo', 0).and_return([0, "user_u:role_r:type_t:s0"]) + expect(get_selinux_default_context_with_handle("/foo", hnd)).to eq("user_u:role_r:type_t:s0") + end + end + + it "should raise an ArgumentError when handle is nil" do + allow(self).to receive(:selinux_support?).and_return(true) + allow(self).to receive(:selinux_label_support?).and_return(true) + expect{get_selinux_default_context_with_handle("/foo", nil)}.to raise_error(ArgumentError, /Cannot get default context with nil handle/) + end + + it "should return nil if there is no SELinux support" do + expect(self).to receive(:selinux_support?).and_return(false) + expect(get_selinux_default_context_with_handle("/foo", nil)).to be_nil + end + + it "should return nil if selinux_label_support returns false" do + expect(self).to receive(:selinux_support?).and_return(true) + expect(self).to receive(:find_fs).with("/foo").and_return("nfs") + expect(get_selinux_default_context_with_handle("/foo", nil)).to be_nil + end + end + describe "parse_selinux_context" do it "should return nil if no context is passed" do expect(parse_selinux_context(:seluser, nil)).to be_nil