Skip to content

Commit 05421c5

Browse files
davejbaxjoshcooper
authored andcommitted
Infer file mode when doing selabel lookup
SELinux file contexts can be limited to files with a particular mode, such as symbolic links only or directories only. Therefore, if we specify no mode (a value of zero), our SELinux label lookup can return an inaccurate result for the file, causing Puppet to set the wrong SELinux type for a file. selabel_file(5) notes this: > mode may be zero, however full matching may not occur. This commit changes the behaviour of get_selinux_default_context_with_handle to attempt to lstat(2) the file, or otherwise rely on the `ensure` property to infer a suitable mode. This should fix #9431.
1 parent d6151b2 commit 05421c5

File tree

4 files changed

+108
-6
lines changed

4 files changed

+108
-6
lines changed

lib/puppet/type/file/selcontext.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def retrieve_default_context(property)
4545
return nil
4646
end
4747

48-
context = get_selinux_default_context_with_handle(@resource[:path], provider.class.selinux_handle)
48+
context = get_selinux_default_context_with_handle(@resource[:path], provider.class.selinux_handle, @resource[:ensure])
4949
unless context
5050
return nil
5151
end

lib/puppet/util/selinux.rb

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ def get_selinux_default_context(file, resource_ensure = nil)
7272
retval == -1 ? nil : retval[1]
7373
end
7474

75-
def get_selinux_default_context_with_handle(file, handle)
75+
def get_selinux_default_context_with_handle(file, handle, resource_ensure = nil)
7676
return nil unless selinux_support?
7777
# If the filesystem has no support for SELinux labels, return a default of nil
7878
# instead of what selabel_lookup would return
@@ -81,7 +81,22 @@ def get_selinux_default_context_with_handle(file, handle)
8181
# Handle is needed for selabel_lookup
8282
raise ArgumentError, _("Cannot get default context with nil handle") unless handle
8383

84-
retval = Selinux.selabel_lookup(handle, file, 0)
84+
# If the file exists we should pass the mode to selabel_lookup for the most specific
85+
# matching. If not, we can pass a mode of 0.
86+
begin
87+
filestat = file_lstat(file)
88+
mode = filestat.mode
89+
rescue Errno::EACCES
90+
mode = 0
91+
rescue Errno::ENOENT
92+
if resource_ensure
93+
mode = get_create_mode(resource_ensure)
94+
else
95+
mode = 0
96+
end
97+
end
98+
99+
retval = Selinux.selabel_lookup(handle, file, mode)
85100
retval == -1 ? nil : retval[1]
86101
end
87102

spec/unit/type/file/selinux_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,15 @@
5151

5252
it "should handle no default gracefully" do
5353
skip if Puppet::Util::Platform.windows?
54-
expect(@sel).to receive(:get_selinux_default_context_with_handle).with(@path, nil).and_return(nil)
54+
expect(@sel).to receive(:get_selinux_default_context_with_handle).with(@path, nil, :file).and_return(nil)
5555
expect(@sel.default).to be_nil
5656
end
5757

5858
it "should be able to detect default context on platforms other than Windows", unless: Puppet::Util::Platform.windows? do
5959
allow(@sel).to receive(:debug)
6060
hnd = double("SWIG::TYPE_p_selabel_handle")
6161
allow(@sel.provider.class).to receive(:selinux_handle).and_return(hnd)
62-
expect(@sel).to receive(:get_selinux_default_context_with_handle).with(@path, hnd).and_return("user_u:role_r:type_t:s0")
62+
expect(@sel).to receive(:get_selinux_default_context_with_handle).with(@path, hnd, :file).and_return("user_u:role_r:type_t:s0")
6363
expectedresult = case param
6464
when :seluser; "user_u"
6565
when :selrole; "role_r"

spec/unit/util/selinux_spec.rb

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
describe Puppet::Util::SELinux do
77
include Puppet::Util::SELinux
88

9-
let(:selinux) { double('selinux', is_selinux_enabled: false) }
9+
let(:selinux) { double('selinux', is_selinux_enabled: 0) }
1010

1111
before :each do
1212
stub_const('Selinux', selinux)
@@ -252,6 +252,93 @@
252252
end
253253
end
254254

255+
it "should return nil when permission denied errors are encountered" do
256+
without_partial_double_verification do
257+
expect(self).to receive(:selinux_support?).and_return(true)
258+
expect(self).to receive(:selinux_label_support?).and_return(true)
259+
hnd = double("SWIG::TYPE_p_selabel_handle")
260+
expect(Selinux).to receive(:selabel_lookup).with(hnd, "/root/chuj", 0).and_return(-1)
261+
expect(self).to receive(:file_lstat).with("/root/chuj").and_raise(Errno::EACCES, "/root/chuj")
262+
263+
expect(get_selinux_default_context_with_handle("/root/chuj", hnd)).to be_nil
264+
end
265+
end
266+
267+
it "should return nil when no such file or directory errors are encountered and resource_ensure is unset" do
268+
without_partial_double_verification do
269+
expect(self).to receive(:selinux_support?).and_return(true)
270+
expect(self).to receive(:selinux_label_support?).and_return(true)
271+
hnd = double("SWIG::TYPE_p_selabel_handle")
272+
expect(Selinux).to receive(:selabel_lookup).with(hnd, "/root/chuj", 0).and_return(-1)
273+
expect(self).to receive(:file_lstat).with("/root/chuj").and_raise(Errno::ENOENT, "/root/chuj")
274+
275+
expect(get_selinux_default_context_with_handle("/root/chuj", hnd)).to be_nil
276+
end
277+
end
278+
279+
it "should pass through lstat mode when file exists" do
280+
without_partial_double_verification do
281+
expect(self).to receive(:selinux_support?).and_return(true).twice
282+
expect(self).to receive(:selinux_label_support?).and_return(true).twice
283+
hnd = double("SWIG::TYPE_p_selabel_handle")
284+
fstat = double("File::Stat", :mode => 16384)
285+
expect(Selinux).to receive(:selabel_lookup).with(hnd, "/root/chuj", fstat.mode).and_return([0, "user_u:role_r:type_t:s0"]).twice
286+
expect(self).to receive(:file_lstat).with("/root/chuj").and_return(fstat).twice
287+
288+
expect(get_selinux_default_context_with_handle("/root/chuj", hnd)).to eq("user_u:role_r:type_t:s0")
289+
expect(get_selinux_default_context_with_handle("/root/chuj", hnd, :file)).to eq("user_u:role_r:type_t:s0")
290+
end
291+
end
292+
293+
it "should determine mode based on resource ensure when set to file" do
294+
without_partial_double_verification do
295+
expect(self).to receive(:selinux_support?).and_return(true).twice
296+
expect(self).to receive(:selinux_label_support?).and_return(true).twice
297+
hnd = double("SWIG::TYPE_p_selabel_handle")
298+
expect(Selinux).to receive(:selabel_lookup).with(hnd, "/root/chuj", 32768).and_return(-1).twice
299+
expect(self).to receive(:file_lstat).with("/root/chuj").and_raise(Errno::ENOENT, "/root/chuj").twice
300+
301+
expect(get_selinux_default_context_with_handle("/root/chuj", hnd, :present)).to be_nil
302+
expect(get_selinux_default_context_with_handle("/root/chuj", hnd, :file)).to be_nil
303+
end
304+
end
305+
306+
it "should determine mode based on resource ensure when set to dir" do
307+
without_partial_double_verification do
308+
expect(self).to receive(:selinux_support?).and_return(true)
309+
expect(self).to receive(:selinux_label_support?).and_return(true)
310+
hnd = double("SWIG::TYPE_p_selabel_handle")
311+
expect(Selinux).to receive(:selabel_lookup).with(hnd, "/root/chuj", 16384).and_return(-1)
312+
expect(self).to receive(:file_lstat).with("/root/chuj").and_raise(Errno::ENOENT, "/root/chuj")
313+
314+
expect(get_selinux_default_context_with_handle("/root/chuj", hnd, :directory)).to be_nil
315+
end
316+
end
317+
318+
it "should determine mode based on resource ensure when set to link" do
319+
without_partial_double_verification do
320+
expect(self).to receive(:selinux_support?).and_return(true)
321+
expect(self).to receive(:selinux_label_support?).and_return(true)
322+
hnd = double("SWIG::TYPE_p_selabel_handle")
323+
expect(Selinux).to receive(:selabel_lookup).with(hnd, "/root/chuj", 40960).and_return(-1)
324+
expect(self).to receive(:file_lstat).with("/root/chuj").and_raise(Errno::ENOENT, "/root/chuj")
325+
326+
expect(get_selinux_default_context_with_handle("/root/chuj", hnd, :link)).to be_nil
327+
end
328+
end
329+
330+
it "should determine mode based on resource ensure when set to unknown" do
331+
without_partial_double_verification do
332+
expect(self).to receive(:selinux_support?).and_return(true)
333+
expect(self).to receive(:selinux_label_support?).and_return(true)
334+
hnd = double("SWIG::TYPE_p_selabel_handle")
335+
expect(Selinux).to receive(:selabel_lookup).with(hnd, "/root/chuj", 0).and_return(-1)
336+
expect(self).to receive(:file_lstat).with("/root/chuj").and_raise(Errno::ENOENT, "/root/chuj")
337+
338+
expect(get_selinux_default_context_with_handle("/root/chuj", hnd, "unknown")).to be_nil
339+
end
340+
end
341+
255342
it "should raise an ArgumentError when handle is nil" do
256343
allow(self).to receive(:selinux_support?).and_return(true)
257344
allow(self).to receive(:selinux_label_support?).and_return(true)

0 commit comments

Comments
 (0)