Skip to content

Commit 19bc0de

Browse files
authored
Merge pull request puppetlabs#9448 from joshcooper/fix-file-resource-seltype-9431
Add file mode-awareness to selabel_lookup
2 parents ffb5d57 + bd1c0e5 commit 19bc0de

File tree

4 files changed

+117
-18
lines changed

4 files changed

+117
-18
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: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -55,24 +55,16 @@ def get_selinux_default_context(file, resource_ensure = nil)
5555

5656
# If the file exists we should pass the mode to matchpathcon for the most specific
5757
# matching. If not, we can pass a mode of 0.
58-
begin
59-
filestat = file_lstat(file)
60-
mode = filestat.mode
61-
rescue Errno::EACCES
62-
mode = 0
63-
rescue Errno::ENOENT
64-
if resource_ensure
65-
mode = get_create_mode(resource_ensure)
66-
else
67-
mode = 0
68-
end
69-
end
58+
mode = file_mode(file, resource_ensure)
7059

7160
retval = Selinux.matchpathcon(file, mode)
7261
retval == -1 ? nil : retval[1]
7362
end
7463

75-
def get_selinux_default_context_with_handle(file, handle)
64+
# Retrieve and return the default context of the file using an selinux handle.
65+
# If we don't have SELinux support or if the SELinux call fails to file a
66+
# default then return nil.
67+
def get_selinux_default_context_with_handle(file, handle, resource_ensure = nil)
7668
return nil unless selinux_support?
7769
# If the filesystem has no support for SELinux labels, return a default of nil
7870
# instead of what selabel_lookup would return
@@ -81,7 +73,11 @@ def get_selinux_default_context_with_handle(file, handle)
8173
# Handle is needed for selabel_lookup
8274
raise ArgumentError, _("Cannot get default context with nil handle") unless handle
8375

84-
retval = Selinux.selabel_lookup(handle, file, 0)
76+
# If the file exists we should pass the mode to selabel_lookup for the most specific
77+
# matching. If not, we can pass a mode of 0.
78+
mode = file_mode(file, resource_ensure)
79+
80+
retval = Selinux.selabel_lookup(handle, file, mode)
8581
retval == -1 ? nil : retval[1]
8682
end
8783

@@ -245,6 +241,22 @@ def get_create_mode(resource_ensure)
245241
mode
246242
end
247243

244+
# If the file/directory/symlink exists, return its mode. Otherwise, get the default mode
245+
# that should be used to create the file/directory/symlink taking into account the desired
246+
# file type specified in +resource_ensure+.
247+
def file_mode(file, resource_ensure)
248+
filestat = file_lstat(file)
249+
filestat.mode
250+
rescue Errno::EACCES
251+
0
252+
rescue Errno::ENOENT
253+
if resource_ensure
254+
get_create_mode(resource_ensure)
255+
else
256+
0
257+
end
258+
end
259+
248260
# Internal helper function to read and parse /proc/mounts
249261
def read_mounts
250262
mounts = ''.dup

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)