Skip to content

Commit fe9b634

Browse files
tobias-urdinjoshcooper
authored andcommitted
(PUP-7559) Pass correct mode when lookup SELinux default context
Originally when the file did not exist we simply passed the mode as zero causing the file type bits not to be set which means matchpatchcon(3) won't lookup the type based on the correct type. This changes so that we set the file type bits in the mode_t passed down to the SELinux libraries based on the resource ensure set to present, file, directory or link on the File resource. This fixes [1] and can be observed for SELinux file contexts that modifies files in top directory but only for symlinks for example this [2] httpd file context change. The patch is backward compatible and does not break the API for the methods in the Puppet::Util::SELinux module. [1] https://tickets.puppetlabs.com/browse/PUP-7559 [2] fedora-selinux/selinux-policy-contrib@43318bf
1 parent bbb35f7 commit fe9b634

File tree

4 files changed

+106
-11
lines changed

4 files changed

+106
-11
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].to_s)
4646
unless context
4747
return nil
4848
end

lib/puppet/util/selinux.rb

Lines changed: 31 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,23 @@ 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+
return mode if resource_ensure == 'absent'
217+
case resource_ensure
218+
when "present", "file"
219+
mode = 0 | S_IFREG
220+
when "directory"
221+
mode = 0 | S_IFDIR
222+
when "link"
223+
mode = 0 | S_IFLNK
224+
end
225+
mode
226+
end
227+
201228
# Internal helper function to read and parse /proc/mounts
202229
def read_mounts
203230
mounts = ""

spec/unit/type/file/selinux_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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, @resource[:ensure].to_s).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, @resource[:ensure].to_s).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)