[Pkg-puppet-devel] [SCM] Puppet packaging for Debian branch, master, updated. debian/0.24.6-1-356-g5718585

James Turnbull james at lovedthanlost.net
Fri Jan 23 14:21:37 UTC 2009


The following commit has been merged in the master branch:
commit 3a5dcab28682a1bbf1b71b2d1de39008468b1ca6
Author: Sean E. Millichamp <sean at bruenor.org>
Date:   Sun Nov 2 20:05:57 2008 -0500

    Refactoring of SELinux functions to use native Ruby SELinux interface

diff --git a/lib/puppet/util/selinux.rb b/lib/puppet/util/selinux.rb
index 1487489..b181b35 100644
--- a/lib/puppet/util/selinux.rb
+++ b/lib/puppet/util/selinux.rb
@@ -1,74 +1,55 @@
 # Provides utility functions to help interfaces Puppet to SELinux.
 #
-# Currently this is implemented via the command line tools.  At some
-# point support should be added to use the new SELinux ruby bindings
-# as that will be faster and more reliable then shelling out when they
-# are available.  At this time (2008-09-26) these bindings aren't bundled on
-# any SELinux-using distribution I know of.
+# This requires the very new SELinux Ruby bindings.  These bindings closely
+# mirror the SELinux C library interface.
+#
+# Support for the command line tools is not provided because the performance
+# was abysmal.  At this time (2008-11-02) the only distribution providing
+# these Ruby SELinux bindings which I am aware of is Fedora (in libselinux-ruby).
 
-require 'puppet/util'
+begin
+    require 'selinux'
+rescue LoadError
+    # Nothing
+end
 
 module Puppet::Util::SELinux
 
-    include Puppet::Util
-
     def selinux_support?
-        FileTest.exists?("/selinux/enforce")
+        unless defined? Selinux
+            return false
+        end
+        if Selinux.is_selinux_enabled == 1
+            return true
+        end
+        return false
     end
 
     # Retrieve and return the full context of the file.  If we don't have
-    # SELinux support or if the stat call fails then return nil.
+    # SELinux support or if the SELinux call fails then return nil.
     def get_selinux_current_context(file)
         unless selinux_support?
             return nil
         end
-        context = ""
-        begin
-            execpipe("/usr/bin/stat -c %C #{file}") do |out|
-                out.each do |line|
-                    context << line
-                end
-            end
-        rescue Puppet::ExecutionFailure
-            return nil
-        end
-        context.chomp!
-        # Handle the case that the system seems to have SELinux support but
-        # stat finds unlabled files.
-        if context == "(null)"
+        retval = Selinux.lgetfilecon(file)
+        if retval == -1
             return nil
         end
-        return context
+        return retval[1]
     end
 
-    # Use the matchpathcon command, if present, to return the SELinux context
-    # which the SELinux policy on the system expects the file to have.  We can
-    # use this to obtain a good default context.  If the command does not
-    # exist or the call fails return nil.
-    #
-    # Note: For this command to work a full, non-relative, filesystem path
-    # should be given.
+    # 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.
     def get_selinux_default_context(file)
         unless selinux_support?
             return nil
         end
-        unless FileTest.executable?("/usr/sbin/matchpathcon")
+        filestat = File.lstat(file)
+        retval = Selinux.matchpathcon(file, filestat.mode)
+        if retval == -1
             return nil
         end
-        context = ""
-        begin
-            execpipe("/usr/sbin/matchpathcon #{file}") do |out|
-                out.each do |line|
-                    context << line
-                end
-            end
-        rescue Puppet::ExecutionFailure
-            return nil
-        end
-        # For a successful match, matchpathcon returns two fields separated by
-        # a variable amount of whitespace.  The second field is the full context.
-        context = context.split(/\s/)[1]
-        return context
+        return retval[1]
     end
 
     # Take the full SELinux context returned from the tools and parse it
@@ -91,32 +72,52 @@ module Puppet::Util::SELinux
     end
 
     # This updates the actual SELinux label on the file.  You can update
-    # only a single component or update the entire context.  It is just a
-    # wrapper around the chcon command.
+    # only a single component or update the entire context.
+    # The caveat is that since setting a partial context makes no sense the
+    # file has to already exist.  Puppet (via the File resource) will always
+    # just try to set components, even if all values are specified by the manifest.
+    # I believe that the OS should always provide at least a fall-through context
+    # though on any well-running system.
     def set_selinux_context(file, value, component = false)
         unless selinux_support?
             return nil
         end
-        case component
-            when :seluser
-                flag = "-u"
-            when :selrole
-                flag = "-r"
-            when :seltype
-                flag = "-t"
-            when :selrange
-                flag = "-l"
-            else
-                flag = nil
-        end
 
-        if flag.nil?
-            cmd = ["/usr/bin/chcon","-h",value,file]
+        if component
+            # Must first get existing context to replace a single component
+            context = Selinux.lgetfilecon(file)[1]
+            if context == -1
+                # We can't set partial context components when no context exists
+                # unless/until we can find a way to make Puppet call this method
+                # once for all selinux file label attributes.
+                Puppet.warning "Can't set SELinux context on file unless the file already has some kind of context"
+                return nil
+            end
+            context = context.split(':')
+            case component
+                when :seluser
+                    context[0] = value
+                when :selrole
+                    context[1] = value
+                when :seltype
+                    context[2] = value
+                when :selrange
+                    context[3] = value
+                else
+                    raise ArguementError, "set_selinux_context component must be one of :seluser, :selrole, :seltype, or :selrange"
+            end
+            context = context.join(':')
+        else
+            context = value
+        end
+       
+        retval = Selinux.lsetfilecon(file, context)
+        if retval == 0
+            return true
         else
-            cmd = ["/usr/bin/chcon","-h",flag,value,file]
+            Puppet.warning "Failed to set SELinux context %s on %s" % [context, file]
+            return false
         end
-        execute(cmd)
-        return true
     end
 
     # Since this call relies on get_selinux_default_context it also needs a
diff --git a/spec/unit/util/selinux.rb b/spec/unit/util/selinux.rb
index 7a56f91..076ebd2 100644
--- a/spec/unit/util/selinux.rb
+++ b/spec/unit/util/selinux.rb
@@ -1,6 +1,6 @@
 #!/usr/bin/env ruby
 
-Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") }
+require File.dirname(__FILE__) + '/../../spec_helper'
 
 require 'puppet/util/selinux'
 include Puppet::Util::SELinux
@@ -8,13 +8,19 @@ include Puppet::Util::SELinux
 describe Puppet::Util::SELinux do
 
     describe "selinux_support?" do
+        before :all do
+            if not defined? Selinux
+                Selinux = mock()
+            end
+        end
+
         it "should return :true if this system has SELinux enabled" do
-             FileTest.expects(:exists?).with("/selinux/enforce").returns true
+             Selinux.expects(:is_selinux_enabled).returns 1
              selinux_support?.should be_true
         end
 
         it "should return :false if this system lacks SELinux" do
-             FileTest.expects(:exists?).with("/selinux/enforce").returns false
+             Selinux.expects(:is_selinux_enabled).returns 0
              selinux_support?.should be_false
         end
     end
@@ -27,19 +33,13 @@ describe Puppet::Util::SELinux do
 
         it "should return a context" do
             self.expects(:selinux_support?).returns true
-            self.expects(:execpipe).with("/usr/bin/stat -c %C /foo").yields ["user_u:role_r:type_t:s0\n"]
+            Selinux.expects(:lgetfilecon).with("/foo").returns [0, "user_u:role_r:type_t:s0"]
             get_selinux_current_context("/foo").should == "user_u:role_r:type_t:s0"
         end
 
-        it "should return nil if an exception is raised calling stat" do
+        it "should return nil if lgetfilecon fails" do
             self.expects(:selinux_support?).returns true
-            self.expects(:execpipe).with("/usr/bin/stat -c %C /foo").raises(Puppet::ExecutionFailure, 'error')
-            get_selinux_current_context("/foo").should be_nil
-        end
-
-        it "should return nil if stat finds an unlabeled file" do
-            self.expects(:selinux_support?).returns true
-            self.expects(:execpipe).with("/usr/bin/stat -c %C /foo").yields ["(null)\n"]
+            Selinux.expects(:lgetfilecon).with("/foo").returns -1
             get_selinux_current_context("/foo").should be_nil
         end
     end
@@ -50,23 +50,19 @@ describe Puppet::Util::SELinux do
             get_selinux_default_context("/foo").should be_nil
         end
 
-        it "should return nil if matchpathcon is not executable" do
-            self.expects(:selinux_support?).returns true
-            FileTest.expects(:executable?).with("/usr/sbin/matchpathcon").returns false
-            get_selinux_default_context("/foo").should be_nil
-        end
-
         it "should return a context if a default context exists" do
             self.expects(:selinux_support?).returns true
-            FileTest.expects(:executable?).with("/usr/sbin/matchpathcon").returns true
-            self.expects(:execpipe).with("/usr/sbin/matchpathcon /foo").yields ["/foo\tuser_u:role_r:type_t:s0\n"]
+            fstat = stub 'File::Stat', :mode => 0
+            File.expects(:lstat).with("/foo").returns fstat
+            Selinux.expects(:matchpathcon).with("/foo", 0).returns [0, "user_u:role_r:type_t:s0"]
             get_selinux_default_context("/foo").should == "user_u:role_r:type_t:s0"
         end
 
-        it "should return nil if an exception is raised calling matchpathcon" do
+        it "should return nil if matchpathcon returns failure" do
             self.expects(:selinux_support?).returns true
-            FileTest.expects(:executable?).with("/usr/sbin/matchpathcon").returns true
-            self.expects(:execpipe).with("/usr/sbin/matchpathcon /foo").raises(Puppet::ExecutionFailure, 'error')
+            fstat = stub 'File::Stat', :mode => 0
+            File.expects(:lstat).with("/foo").returns fstat
+            Selinux.expects(:matchpathcon).with("/foo", 0).returns -1
             get_selinux_default_context("/foo").should be_nil
         end
     end
@@ -115,33 +111,37 @@ describe Puppet::Util::SELinux do
             set_selinux_context("/foo", "user_u:role_r:type_t:s0").should be_nil
         end
 
-        it "should use chcon to set a context" do
+        it "should use lsetfilecon to set a context" do
             self.expects(:selinux_support?).returns true
-            self.expects(:execute).with(["/usr/bin/chcon","-h","user_u:role_r:type_t:s0","/foo"]).returns 0
+            Selinux.expects(:lsetfilecon).with("/foo", "user_u:role_r:type_t:s0").returns 0
             set_selinux_context("/foo", "user_u:role_r:type_t:s0").should be_true
         end
 
-        it "should use chcon to set user_u user context" do
+        it "should use lsetfilecon to set user_u user context" do
             self.expects(:selinux_support?).returns true
-            self.expects(:execute).with(["/usr/bin/chcon","-h","-u","user_u","/foo"]).returns 0
+            Selinux.expects(:lgetfilecon).with("/foo").returns [0, "foo:role_r:type_t:s0"]
+            Selinux.expects(:lsetfilecon).with("/foo", "user_u:role_r:type_t:s0").returns 0
             set_selinux_context("/foo", "user_u", :seluser).should be_true
         end
 
-        it "should use chcon to set role_r role context" do
+        it "should use lsetfilecon to set role_r role context" do
             self.expects(:selinux_support?).returns true
-            self.expects(:execute).with(["/usr/bin/chcon","-h","-r","role_r","/foo"]).returns 0
+            Selinux.expects(:lgetfilecon).with("/foo").returns [0, "user_u:foo:type_t:s0"]
+            Selinux.expects(:lsetfilecon).with("/foo", "user_u:role_r:type_t:s0").returns 0
             set_selinux_context("/foo", "role_r", :selrole).should be_true
         end
 
-        it "should use chcon to set type_t type context" do
+        it "should use lsetfilecon to set type_t type context" do
             self.expects(:selinux_support?).returns true
-            self.expects(:execute).with(["/usr/bin/chcon","-h","-t","type_t","/foo"]).returns 0
+            Selinux.expects(:lgetfilecon).with("/foo").returns [0, "user_u:role_r:foo:s0"]
+            Selinux.expects(:lsetfilecon).with("/foo", "user_u:role_r:type_t:s0").returns 0
             set_selinux_context("/foo", "type_t", :seltype).should be_true
         end
 
-        it "should use chcon to set s0:c3,c5 range context" do
+        it "should use lsetfilecon to set s0:c3,c5 range context" do
             self.expects(:selinux_support?).returns true
-            self.expects(:execute).with(["/usr/bin/chcon","-h","-l","s0:c3,c5","/foo"]).returns 0
+            Selinux.expects(:lgetfilecon).with("/foo").returns [0, "user_u:role_r:type_t:s0"]
+            Selinux.expects(:lsetfilecon).with("/foo", "user_u:role_r:type_t:s0:c3,c5").returns 0
             set_selinux_context("/foo", "s0:c3,c5", :selrange).should be_true
         end
     end

-- 
Puppet packaging for Debian



More information about the Pkg-puppet-devel mailing list