Skip to content

Commit e1982d6

Browse files
authored
Merge pull request #8570 from joshcooper/selinux_7559
(PUP-7559) Pass correct mode when lookup SELinux default context
2 parents c2523bb + f62b162 commit e1982d6

File tree

4 files changed

+106
-12
lines changed

4 files changed

+106
-12
lines changed

lib/puppet/type/file/selcontext.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def retrieve_default_context(property)
4242
return nil
4343
end
4444

45-
context = self.get_selinux_default_context(@resource[:path])
45+
context = self.get_selinux_default_context(@resource[:path], @resource[:ensure])
4646
unless context
4747
return nil
4848
end

lib/puppet/util/selinux.rb

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313

1414
module Puppet::Util::SELinux
1515

16+
S_IFREG = 0100000
17+
S_IFDIR = 0040000
18+
S_IFLNK = 0120000
19+
1620
def self.selinux_support?
1721
return false unless defined?(Selinux)
1822
if Selinux.is_selinux_enabled == 1
@@ -38,7 +42,7 @@ def get_selinux_current_context(file)
3842

3943
# Retrieve and return the default context of the file. If we don't have
4044
# SELinux support or if the SELinux call fails to file a default then return nil.
41-
def get_selinux_default_context(file)
45+
def get_selinux_default_context(file, resource_ensure=nil)
4246
return nil unless selinux_support?
4347
# If the filesystem has no support for SELinux labels, return a default of nil
4448
# instead of what matchpathcon would return
@@ -48,8 +52,14 @@ def get_selinux_default_context(file)
4852
begin
4953
filestat = file_lstat(file)
5054
mode = filestat.mode
51-
rescue Errno::EACCES, Errno::ENOENT
55+
rescue Errno::EACCES
5256
mode = 0
57+
rescue Errno::ENOENT
58+
if resource_ensure
59+
mode = get_create_mode(resource_ensure)
60+
else
61+
mode = 0
62+
end
5363
end
5464

5565
retval = Selinux.matchpathcon(file, mode)
@@ -136,8 +146,8 @@ def set_selinux_context(file, value, component = false)
136146
# Puppet uses. This will set the file's SELinux context to the policy's
137147
# default context (if any) if it differs from the context currently on
138148
# the file.
139-
def set_selinux_default_context(file)
140-
new_context = get_selinux_default_context(file)
149+
def set_selinux_default_context(file, resource_ensure=nil)
150+
new_context = get_selinux_default_context(file, resource_ensure)
141151
return nil unless new_context
142152
cur_context = get_selinux_current_context(file)
143153
if new_context != cur_context
@@ -198,6 +208,22 @@ def selinux_label_support?(file)
198208
filesystems.include?(fstype)
199209
end
200210

211+
# Get mode file type bits set based on ensure on
212+
# the file resource. This helps SELinux determine
213+
# what context a new resource being created should have.
214+
def get_create_mode(resource_ensure)
215+
mode = 0
216+
case resource_ensure
217+
when :present, :file
218+
mode |= S_IFREG
219+
when :directory
220+
mode |= S_IFDIR
221+
when :link
222+
mode |= S_IFLNK
223+
end
224+
mode
225+
end
226+
201227
# Internal helper function to read and parse /proc/mounts
202228
def read_mounts
203229
mounts = ""

spec/unit/type/file/selinux_spec.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
before do
99
@path = make_absolute("/my/file")
10-
@resource = Puppet::Type.type(:file).new :path => @path
10+
@resource = Puppet::Type.type(:file).new(:path => @path, :ensure => :file)
1111
@sel = property.new :resource => @resource
1212
end
1313

@@ -50,13 +50,13 @@
5050
end
5151

5252
it "should handle no default gracefully" do
53-
expect(@sel).to receive(:get_selinux_default_context).with(@path).and_return(nil)
53+
expect(@sel).to receive(:get_selinux_default_context).with(@path, :file).and_return(nil)
5454
expect(@sel.default).to be_nil
5555
end
5656

5757
it "should be able to detect matchpathcon defaults" do
5858
allow(@sel).to receive(:debug)
59-
expect(@sel).to receive(:get_selinux_default_context).with(@path).and_return("user_u:role_r:type_t:s0")
59+
expect(@sel).to receive(:get_selinux_default_context).with(@path, :file).and_return("user_u:role_r:type_t:s0")
6060
expectedresult = case param
6161
when :seluser; "user_u"
6262
when :selrole; "role_r"

spec/unit/util/selinux_spec.rb

Lines changed: 72 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@
159159
end
160160
end
161161

162-
it "handles no such file or directory errors by issuing a warning" do
162+
it "backward compatibly handles no such file or directory errors by issuing a warning when resource_ensure not set" do
163163
without_partial_double_verification do
164164
allow(self).to receive(:selinux_support?).and_return(true)
165165
allow(self).to receive(:selinux_label_support?).and_return(true)
@@ -170,6 +170,51 @@
170170
end
171171
end
172172

173+
it "should determine mode based on resource ensure when set to file" do
174+
without_partial_double_verification do
175+
allow(self).to receive(:selinux_support?).and_return(true)
176+
allow(self).to receive(:selinux_label_support?).and_return(true)
177+
allow(Selinux).to receive(:matchpathcon).with("/root/chuj", 32768).and_return(-1)
178+
allow(self).to receive(:file_lstat).with("/root/chuj").and_raise(Errno::ENOENT, "/root/chuj")
179+
180+
expect(get_selinux_default_context("/root/chuj", :present)).to be_nil
181+
expect(get_selinux_default_context("/root/chuj", :file)).to be_nil
182+
end
183+
end
184+
185+
it "should determine mode based on resource ensure when set to dir" do
186+
without_partial_double_verification do
187+
allow(self).to receive(:selinux_support?).and_return(true)
188+
allow(self).to receive(:selinux_label_support?).and_return(true)
189+
allow(Selinux).to receive(:matchpathcon).with("/root/chuj", 16384).and_return(-1)
190+
allow(self).to receive(:file_lstat).with("/root/chuj").and_raise(Errno::ENOENT, "/root/chuj")
191+
192+
expect(get_selinux_default_context("/root/chuj", :directory)).to be_nil
193+
end
194+
end
195+
196+
it "should determine mode based on resource ensure when set to link" do
197+
without_partial_double_verification do
198+
allow(self).to receive(:selinux_support?).and_return(true)
199+
allow(self).to receive(:selinux_label_support?).and_return(true)
200+
allow(Selinux).to receive(:matchpathcon).with("/root/chuj", 40960).and_return(-1)
201+
allow(self).to receive(:file_lstat).with("/root/chuj").and_raise(Errno::ENOENT, "/root/chuj")
202+
203+
expect(get_selinux_default_context("/root/chuj", :link)).to be_nil
204+
end
205+
end
206+
207+
it "should determine mode based on resource ensure when set to unknown" do
208+
without_partial_double_verification do
209+
allow(self).to receive(:selinux_support?).and_return(true)
210+
allow(self).to receive(:selinux_label_support?).and_return(true)
211+
allow(Selinux).to receive(:matchpathcon).with("/root/chuj", 0).and_return(-1)
212+
allow(self).to receive(:file_lstat).with("/root/chuj").and_raise(Errno::ENOENT, "/root/chuj")
213+
214+
expect(get_selinux_default_context("/root/chuj", "unknown")).to be_nil
215+
end
216+
end
217+
173218
it "should return nil if matchpathcon returns failure" do
174219
without_partial_double_verification do
175220
expect(self).to receive(:selinux_support?).and_return(true)
@@ -329,21 +374,44 @@
329374
end
330375

331376
it "should return nil if no default context exists" do
332-
expect(self).to receive(:get_selinux_default_context).with("/foo").and_return(nil)
377+
expect(self).to receive(:get_selinux_default_context).with("/foo", nil).and_return(nil)
333378
expect(set_selinux_default_context("/foo")).to be_nil
334379
end
335380

336381
it "should do nothing and return nil if the current context matches the default context" do
337-
expect(self).to receive(:get_selinux_default_context).with("/foo").and_return("user_u:role_r:type_t")
382+
expect(self).to receive(:get_selinux_default_context).with("/foo", nil).and_return("user_u:role_r:type_t")
338383
expect(self).to receive(:get_selinux_current_context).with("/foo").and_return("user_u:role_r:type_t")
339384
expect(set_selinux_default_context("/foo")).to be_nil
340385
end
341386

342387
it "should set and return the default context if current and default do not match" do
343-
expect(self).to receive(:get_selinux_default_context).with("/foo").and_return("user_u:role_r:type_t")
388+
expect(self).to receive(:get_selinux_default_context).with("/foo", nil).and_return("user_u:role_r:type_t")
344389
expect(self).to receive(:get_selinux_current_context).with("/foo").and_return("olduser_u:role_r:type_t")
345390
expect(self).to receive(:set_selinux_context).with("/foo", "user_u:role_r:type_t").and_return(true)
346391
expect(set_selinux_default_context("/foo")).to eq("user_u:role_r:type_t")
347392
end
348393
end
394+
395+
describe "get_create_mode" do
396+
it "should return 0 if the resource is absent" do
397+
expect(get_create_mode(:absent)).to eq(0)
398+
end
399+
400+
it "should return mode with file type set to S_IFREG when resource is file" do
401+
expect(get_create_mode(:present)).to eq(32768)
402+
expect(get_create_mode(:file)).to eq(32768)
403+
end
404+
405+
it "should return mode with file type set to S_IFDIR when resource is dir" do
406+
expect(get_create_mode(:directory)).to eq(16384)
407+
end
408+
409+
it "should return mode with file type set to S_IFLNK when resource is link" do
410+
expect(get_create_mode(:link)).to eq(40960)
411+
end
412+
413+
it "should return 0 for everything else" do
414+
expect(get_create_mode("unknown")).to eq(0)
415+
end
416+
end
349417
end

0 commit comments

Comments
 (0)