Object
Provide safe access to SELinux library
libselinuxmatchpathcon is written such that there is a thread-local handle
+static __thread struct selabel_handle *hnd+. When #matchpathcon is invoked, it checks to see if the thread-local handle is NULL. If so, it calls #matchpathcon_init_prefix, which sets a non-NULL value for the pthread specific destructor_key. Once this is non-NULL, it means that when the thread terminates, #matchpathcon_fini will execute. When this happens, it frees memory that is NOT declared +__thread (SELABELSUB *selabelsublist)+. As a result, because there is no mutex guarding access to +selabelsublist+, it is possible for 1 thread to be calling #matchpathcon (which uses +selabelsublist+) while another thread is freeing the memory associated with +selabelsublist+, resulting in segfault either due to an invalid memory address, or an attempt to double free the memory.
# File lib/openshift-origin-node/utils/selinux_context.rb, line 98 def initialize @context = Hash.new @context_mutex = Mutex.new @query = ConditionVariable.new @response = ConditionVariable.new @thread = matchpathcon_worker config = ::OpenShift::Config.new @set_size = (config.get('SELINUX_MCS_SET_SIZE') || 1024).to_i @group_size= (config.get('SELINUX_MCS_GROUP_SIZE') || 2).to_i @uid_offset= (config.get('SELINUX_MCS_UID_OFFSET') || 0).to_i @mls_num = (config.get('SELINUX_MLS_NUM') || 0).to_i end
Set the context of a single file or directory.
Where a portion of the context is not provided on the command line, it will be determined from the file context database or the file itself.
@param [String] path file to set selinux @param [String] level selinux level to set for path, defaults to OpenShift policy @param [String] type selinux type to set for path, defaults to selinux policy @param [String] role selinux role to set for path, defaults to selinux policy @param [String] user selinux user to set for path, defaults to selinux policy
# File lib/openshift-origin-node/utils/selinux_context.rb, line 257 def chcon(path, level=nil, type=nil, role=nil, user=nil) expected, actual = path_context(path) Selinux.context_range_set(expected, level) unless level.nil? Selinux.context_type_set(expected, type) unless type.nil? Selinux.context_role_set(expected, role) unless role.nil? Selinux.context_user_set(expected, user) unless user.nil? range_eq = Selinux.context_range_get(expected) == Selinux.context_range_get(actual) return if expected == actual && range_eq return if -1 != Selinux.lsetfilecon(path, expected.to_s) err = "Could not set the file context #{expected} on #{path}" NodeLogger.logger.error(err) raise Errno::EINVAL.new(err) end
Clear the SELinux context of any MCS label.
# File lib/openshift-origin-node/utils/selinux_context.rb, line 334 def clear_mcs_label(*paths) set_mcs_label(nil, *paths) end
Recursively clear the SELinux context of any MCS label.
# File lib/openshift-origin-node/utils/selinux_context.rb, line 340 def clear_mcs_label_R(*paths) set_mcs_label_R(nil, *paths) end
Create a context from defaults.
@param [String] level selinux level, defaults to OpenShift policy @param [String] type selinux type, defaults to OpenShift policy @param [String] role selinux role, defaults to OpenShift policy @param [String] user selinux user, defaults to OpenShift policy
# File lib/openshift-origin-node/utils/selinux_context.rb, line 350 def from_defaults(level=nil, type=nil, role=nil, user=nil) t_label = (level || DefaultLevel).to_s t_type = (type || DefaultType).to_s t_role = (role || DefaultRole).to_s t_user = (user || DefaultUser).to_s "#{t_user}:#{t_role}:#{t_type}:#{t_label}" end
Determine the MCS label for a given index
@param name [String, Integer] The user name or uid @return [String] The SELinux MCS label
# File lib/openshift-origin-node/utils/selinux_context.rb, line 209 def get_mcs_label(name) begin uid = EtcUtils.uid(name) rescue ArgumentError, TypeError, NoMethodError raise ArgumentError, "Argument must be a numeric UID or existing username: #{name}" end if uid < uid_offset + group_size - 1 raise ArgumentError, "Argument must resolve to a UID greater than #{uid_offset + group_size - 1}: #{name}" end if group_size == 2 if uid < uid_offset + set_size * (set_size - 1) / 2 # offset uid ouid = uid - uid_offset # Quadratic formula a = 1 # This is actually negative b, which is what you want b = 2 * set_size - 1 c = (2 * ouid - 2) # Root of the equation root = ((b - Math::sqrt(b**2 - 4*a*c)) / (2 * a)).to_i # remainder remainder = (ouid - (2*set_size - root - 1) * root / 2) + root return "s#{mls_num}:c#{root},c#{remainder}" end else mcs_labels.each do |tuid, label| if uid == tuid return label end end end raise ArgumentError, "Argument resolved to a UID too large for MCS set parameters: #{uid}" end
Get the current context
@return [Context_s_t] Selinux context for process
# File lib/openshift-origin-node/utils/selinux_context.rb, line 361 def getcon Selinux.getcon[1] end
get the default SELinux security context for the specified path from the file contexts configuration
This is the thread safe method that will delegate work to the background thread holding the connection
to the libselinux library.
@param path [String] Path to query against @param mode [Fixnum] mode for path @return [Selinux#Context_s_t] default security context associated with the path
# File lib/openshift-origin-node/utils/selinux_context.rb, line 166 def matchpathcon(path, mode) @context_mutex.synchronize do # We queue up our request in the hash for processing @context[Thread.current.object_id] = MatchPathContext.new(Thread.current.object_id, path, mode) # wake up the worker to call the library @query.signal while @context[Thread.current.object_id].context.nil? # wait until the worker publishes any results. # @note false wake ups are possible, so we go back to sleep if our results are ready. @response.wait(@context_mutex) end # We have our results! Remove the request and return the results. return @context.delete(Thread.current.object_id).context end end
Instantiate a background thread for holding the connection to the libselinuxmatchpath* functions
@note This must be the only access to the libselinuxmatchpath* functions. Otherwise, the library will
seg fault. See above.
@see ConditionVariable, Mutex
@return [Thread]
# File lib/openshift-origin-node/utils/selinux_context.rb, line 121 def matchpathcon_worker Thread.start do # Now we're in a safe place, initialize the matchpathcon context. # @note Never do this again anywhere in this process. Or you will be seg faulted. Selinux.matchpathcon_init(nil) # Yes, this worker will live until the process is killed loop do # Worker and subscribers synchronize on +mutex+ to ensure one writer at a time changes @context @context_mutex.synchronize do # @note false wake ups are possible so we sleep until we know there is work queued up # Also, @context may not be empty, but all the requests may have been handled and we're # just waiting for the callers to pick them up, so make sure we wait if every value's # .context is filled in while @context.empty? or @context.values.all? { |v| not v.context.nil? } # Wait for a +signal+ from a #matchpathcon call if there is no work queued up for us @query.wait(@context_mutex) end broadcast = false @context.keys.each do |key| # Process all queued requests, Later we'll wakeup all the sleepers... if @context[key].context.nil? # Because we're in the +mutex+ and a subscriber has signaled us, it is safe to call # libselinux#matchpathcon() and update context hash with results @context[key].context = Selinux.matchpathcon(@context[key].path, @context[key].mode) broadcast = true end end # We did some work, so tell anyone who may be waiting @response.broadcast if broadcast end end end end
Return an enumerator which yields each UID -> MCS label combination.
Provides a more efficient way to iterate through all of the available ones than re-running the combinations each time.
# File lib/openshift-origin-node/utils/selinux_context.rb, line 190 def mcs_labels Enumerator.new do |yielder| iuid = uid_offset + group_size - 1 set_size.times.to_a.combination(group_size) do |c| mcs_label = c.sort.map { |i| "c#{i}" }.join(",") mls_label = "s#{mls_num}" yielder.yield([iuid, "#{mls_label}:#{mcs_label}"]) iuid +=1 end end end
Retrieve the default context for an object on the file system
@param [String] path of file or directory @return [Array<Context_s_t, Context_s_t>] The expected context and actual context
# File lib/openshift-origin-node/utils/selinux_context.rb, line 277 def path_context(path) mode = File.lstat(path).mode & 07777 actual = Selinux.lgetfilecon(path) expected = matchpathcon(path, mode) if -1 == expected if -1 == actual err = "Could not read or determine the file context for #{path}" NodeLogger.logger.error(err) raise Errno::EINVAL.new(err) else expected = actual end end return Selinux.context_new(expected[1]), Selinux.context_new(actual[1]) end
reset Singleton @note Only to be used during testing
# File lib/openshift-origin-node/utils/selinux_context.rb, line 367 def reset Singleton.__init__(self) end
Set the SELinux context with provided MCS label on a given set of files.
Acts on the symbolic link itself instead of dereferencing.
Globs must be dereferenced but can be provided as an argument. @example
set_mcs_label("s0:c1,c2", Dir.glob("/path/to/gear/*"))
@param [String] label for files @param [Array<String>] paths for files
# File lib/openshift-origin-node/utils/selinux_context.rb, line 306 def set_mcs_label(label, *paths) paths.flatten.each do |path| chcon(path, label) end end
Recursively set SELinux context with provided MCS label on a given set of files.
Will not dereference symbolic links either as a parameter or as a discovered file.
Globs must be dereferenced but can be provided as an argument. @example
set_mcs_label_R("s0:c1,c2", Dir.glob("/path/to/gear/*"))
@param [String] label for files @param [Arrary<Strings>] paths for files
# File lib/openshift-origin-node/utils/selinux_context.rb, line 324 def set_mcs_label_R(label, *paths) paths.flatten.each do |path| Find.find(path) do |fpath| chcon(fpath, label) end end end
Generated with the Darkfish Rdoc Generator 2.