Blame SOURCES/bz1253294-01-fixed-command-injection-vulnerability.patch

71541a
From b47f6196aaf405f17197d4bb312d94ec84042343 Mon Sep 17 00:00:00 2001
71541a
From: Tomas Jelinek <tojeline@redhat.com>
71541a
Date: Tue, 25 Aug 2015 16:46:46 +0200
71541a
Subject: [PATCH] fixed command injection vulnerability
71541a
71541a
---
71541a
 pcsd/fenceagent.rb | 53 ++++++++++++++++++++++++++++++++++-------------------
71541a
 pcsd/pcsd.rb       |  6 +++---
71541a
 pcsd/remote.rb     | 18 +++++++++---------
71541a
 pcsd/resource.rb   | 27 +++++++++++++++++++++++----
71541a
 4 files changed, 69 insertions(+), 35 deletions(-)
71541a
71541a
diff --git a/pcsd/fenceagent.rb b/pcsd/fenceagent.rb
71541a
index b7674fd..b52ad6f 100644
71541a
--- a/pcsd/fenceagent.rb
71541a
+++ b/pcsd/fenceagent.rb
71541a
@@ -1,4 +1,4 @@
71541a
-def getFenceAgents(fence_agent = nil)
71541a
+def getFenceAgents(session, fence_agent = nil)
71541a
   fence_agent_list = {}
71541a
   agents = Dir.glob('/usr/sbin/fence_' + '*')
71541a
   agents.each { |a|
71541a
@@ -7,7 +7,7 @@ def getFenceAgents(fence_agent = nil)
71541a
     next if fa.name == "fence_ack_manual"
71541a
 
71541a
     if fence_agent and a.sub(/.*\//,"") == fence_agent.sub(/.*:/,"")
71541a
-      required_options, optional_options, advanced_options, info = getFenceAgentMetadata(fa.name)
71541a
+      required_options, optional_options, advanced_options, info = getFenceAgentMetadata(session, fa.name)
71541a
       fa.required_options = required_options
71541a
       fa.optional_options = optional_options
71541a
       fa.advanced_options = advanced_options
71541a
@@ -18,13 +18,42 @@ def getFenceAgents(fence_agent = nil)
71541a
   fence_agent_list
71541a
 end
71541a
 
71541a
-def getFenceAgentMetadata(fenceagentname)
71541a
+def getFenceAgentMetadata(session, fenceagentname)
71541a
+  options_required = {}
71541a
+  options_optional = {}
71541a
+  options_advanced = {
71541a
+      "priority" => "",
71541a
+      "pcmk_host_argument" => "",
71541a
+      "pcmk_host_map" => "",
71541a
+      "pcmk_host_list" => "",
71541a
+      "pcmk_host_check" => ""
71541a
+  }
71541a
+  for a in ["reboot", "list", "status", "monitor", "off"]
71541a
+    options_advanced["pcmk_" + a + "_action"] = ""
71541a
+    options_advanced["pcmk_" + a + "_timeout"] = ""
71541a
+    options_advanced["pcmk_" + a + "_retries"] = ""
71541a
+  end
71541a
+
71541a
   # There are bugs in stonith_admin & the new fence_agents interaction
71541a
   # eventually we'll want to switch back to this, but for now we directly
71541a
   # call the agent to get metadata
71541a
   #metadata = `stonith_admin --metadata -a #{fenceagentname}`
71541a
-  metadata = `/usr/sbin/#{fenceagentname} -o metadata`
71541a
-  doc = REXML::Document.new(metadata)
71541a
+  if not fenceagentname.start_with?('fence_') or fenceagentname.include?('/')
71541a
+    $logger.error "Invalid fence agent '#{fenceagentname}'"
71541a
+    return [options_required, options_optional, options_advanced]
71541a
+  end
71541a
+  stdout, stderr, retval = run_cmd(
71541a
+    session, "/usr/sbin/#{fenceagentname}", '-o', 'metadata'
71541a
+  )
71541a
+  metadata = stdout.join
71541a
+  begin
71541a
+    doc = REXML::Document.new(metadata)
71541a
+  rescue REXML::ParseException => e
71541a
+    $logger.error(
71541a
+      "Unable to parse metadata of fence agent '#{resourcepath}': #{e}"
71541a
+    )
71541a
+    return [options_required, options_optional, options_advanced]
71541a
+  end
71541a
 
71541a
   short_desc = ""
71541a
   long_desc = ""
71541a
@@ -40,20 +69,6 @@ def getFenceAgentMetadata(fenceagentname)
71541a
     long_desc = ld.text ? ld.text.strip : ld.text
71541a
   }
71541a
 
71541a
-  options_required = {}
71541a
-  options_optional = {}
71541a
-  options_advanced = {
71541a
-      "priority" => "",
71541a
-      "pcmk_host_argument" => "",
71541a
-      "pcmk_host_map" => "",
71541a
-      "pcmk_host_list" => "",
71541a
-      "pcmk_host_check" => ""
71541a
-  }
71541a
-  for a in ["reboot", "list", "status", "monitor", "off"]
71541a
-    options_advanced["pcmk_" + a + "_action"] = ""
71541a
-    options_advanced["pcmk_" + a + "_timeout"] = ""
71541a
-    options_advanced["pcmk_" + a + "_retries"] = ""
71541a
-  end
71541a
   doc.elements.each('resource-agent/parameters/parameter') { |param|
71541a
     temp_array = []
71541a
     if param.elements["shortdesc"]
71541a
diff --git a/pcsd/pcsd.rb b/pcsd/pcsd.rb
71541a
index e4b4c25..1f26fe5 100644
71541a
--- a/pcsd/pcsd.rb
71541a
+++ b/pcsd/pcsd.rb
71541a
@@ -401,7 +401,7 @@ if not DISABLE_GUI
71541a
 
71541a
     if @resources.length == 0
71541a
       @cur_resource = nil
71541a
-      @resource_agents = getFenceAgents()
71541a
+      @resource_agents = getFenceAgents(session)
71541a
     else
71541a
       @cur_resource = @resources[0]
71541a
       if params[:fencedevice]
71541a
@@ -413,7 +413,7 @@ if not DISABLE_GUI
71541a
         end
71541a
       end
71541a
       @cur_resource.options = getResourceOptions(session, @cur_resource.id)
71541a
-      @resource_agents = getFenceAgents(@cur_resource.agentname)
71541a
+      @resource_agents = getFenceAgents(session, @cur_resource.agentname)
71541a
     end
71541a
     erb :fencedevices, :layout => :main
71541a
   end
71541a
@@ -477,7 +477,7 @@ if not DISABLE_GUI
71541a
     #    }
71541a
     #  }
71541a
     @resource_agents = getResourceAgents(session)
71541a
-    @stonith_agents = getFenceAgents()
71541a
+    @stonith_agents = getFenceAgents(session)
71541a
     #  @nodes = @nodes.sort_by{|k,v|k}
71541a
     erb :nodes, :layout => :main
71541a
   end
71541a
diff --git a/pcsd/remote.rb b/pcsd/remote.rb
71541a
index cb5b176..4655756 100644
71541a
--- a/pcsd/remote.rb
71541a
+++ b/pcsd/remote.rb
71541a
@@ -1370,11 +1370,11 @@ def resource_form(params, request, session)
71541a
     @cur_resource_ms = @cur_resource.get_master
71541a
     @resource = ResourceAgent.new(@cur_resource.agentname)
71541a
     if @cur_resource.provider == 'heartbeat'
71541a
-      @resource.required_options, @resource.optional_options, @resource.info = getResourceMetadata(HEARTBEAT_AGENTS_DIR + @cur_resource.type)
71541a
+      @resource.required_options, @resource.optional_options, @resource.info = getResourceMetadata(session, HEARTBEAT_AGENTS_DIR + @cur_resource.type)
71541a
     elsif @cur_resource.provider == 'pacemaker'
71541a
-      @resource.required_options, @resource.optional_options, @resource.info = getResourceMetadata(PACEMAKER_AGENTS_DIR + @cur_resource.type)
71541a
+      @resource.required_options, @resource.optional_options, @resource.info = getResourceMetadata(session, PACEMAKER_AGENTS_DIR + @cur_resource.type)
71541a
     elsif @cur_resource._class == 'nagios'
71541a
-      @resource.required_options, @resource.optional_options, @resource.info = getResourceMetadata(NAGIOS_METADATA_DIR + @cur_resource.type + '.xml')
71541a
+      @resource.required_options, @resource.optional_options, @resource.info = getResourceMetadata(session, NAGIOS_METADATA_DIR + @cur_resource.type + '.xml')
71541a
     end
71541a
     @existing_resource = true
71541a
     if @resource
71541a
@@ -1395,7 +1395,7 @@ def fence_device_form(params, request, session)
71541a
   @cur_resource = get_resource_by_id(params[:resource], get_cib_dom(session))
71541a
 
71541a
   if @cur_resource.instance_of?(ClusterEntity::Primitive) and @cur_resource.stonith
71541a
-    @resource_agents = getFenceAgents(@cur_resource.agentname)
71541a
+    @resource_agents = getFenceAgents(session, @cur_resource.agentname)
71541a
     @existing_resource = true
71541a
     @fenceagent = @resource_agents[@cur_resource.type]
71541a
     erb :fenceagentform
71541a
@@ -1531,7 +1531,7 @@ def get_avail_fence_agents(params, request, session)
71541a
   if not allowed_for_local_cluster(session, Permissions::READ)
71541a
     return 403, 'Permission denied'
71541a
   end
71541a
-  agents = getFenceAgents()
71541a
+  agents = getFenceAgents(session)
71541a
   return JSON.generate(agents)
71541a
 end
71541a
 
71541a
@@ -1545,11 +1545,11 @@ def resource_metadata(params, request, session)
71541a
 
71541a
   @resource = ResourceAgent.new(params[:resourcename])
71541a
   if class_provider == "ocf:heartbeat"
71541a
-    @resource.required_options, @resource.optional_options, @resource.info = getResourceMetadata(HEARTBEAT_AGENTS_DIR + resource_name)
71541a
+    @resource.required_options, @resource.optional_options, @resource.info = getResourceMetadata(session, HEARTBEAT_AGENTS_DIR + resource_name)
71541a
   elsif class_provider == "ocf:pacemaker"
71541a
-    @resource.required_options, @resource.optional_options, @resource.info = getResourceMetadata(PACEMAKER_AGENTS_DIR + resource_name)
71541a
+    @resource.required_options, @resource.optional_options, @resource.info = getResourceMetadata(session, PACEMAKER_AGENTS_DIR + resource_name)
71541a
   elsif class_provider == 'nagios'
71541a
-    @resource.required_options, @resource.optional_options, @resource.info = getResourceMetadata(NAGIOS_METADATA_DIR + resource_name + '.xml')
71541a
+    @resource.required_options, @resource.optional_options, @resource.info = getResourceMetadata(session, NAGIOS_METADATA_DIR + resource_name + '.xml')
71541a
   end
71541a
   @new_resource = params[:new]
71541a
   @resources, @groups = getResourcesGroups(session)
71541a
@@ -1563,7 +1563,7 @@ def fence_device_metadata(params, request, session)
71541a
   end
71541a
   return 200 if not params[:resourcename] or params[:resourcename] == ""
71541a
   @fenceagent = FenceAgent.new(params[:resourcename])
71541a
-  @fenceagent.required_options, @fenceagent.optional_options, @fenceagent.advanced_options, @fenceagent.info = getFenceAgentMetadata(params[:resourcename])
71541a
+  @fenceagent.required_options, @fenceagent.optional_options, @fenceagent.advanced_options, @fenceagent.info = getFenceAgentMetadata(session, params[:resourcename])
71541a
   @new_fenceagent = params[:new]
71541a
   
71541a
   erb :fenceagentform
71541a
diff --git a/pcsd/resource.rb b/pcsd/resource.rb
71541a
index c6b513b..6f8f7fe 100644
71541a
--- a/pcsd/resource.rb
71541a
+++ b/pcsd/resource.rb
71541a
@@ -1,4 +1,5 @@
71541a
 require 'pp'
71541a
+require 'pathname'
71541a
 
71541a
 def getResourcesGroups(session, get_fence_devices = false, get_all_options = false,
71541a
   get_operations=false
71541a
@@ -302,12 +303,24 @@ def getColocationConstraints(session, resource_id)
71541a
   return together,apart
71541a
 end
71541a
 
71541a
-def getResourceMetadata(resourcepath)
71541a
+def getResourceMetadata(session, resourcepath)
71541a
   options_required = {}
71541a
   options_optional = {}
71541a
   long_desc = ""
71541a
   short_desc = ""
71541a
 
71541a
+  resourcepath = Pathname.new(resourcepath).cleanpath.to_s
71541a
+  resource_dirs = [
71541a
+    HEARTBEAT_AGENTS_DIR, PACEMAKER_AGENTS_DIR, NAGIOS_METADATA_DIR,
71541a
+  ]
71541a
+  if not resource_dirs.any? { |allowed| resourcepath.start_with?(allowed) }
71541a
+    $logger.error(
71541a
+      "Unable to get metadata of resource agent '#{resourcepath}': " +
71541a
+      'path not allowed'
71541a
+    )
71541a
+    return [options_required, options_optional, [short_desc, long_desc]]
71541a
+  end
71541a
+
71541a
   if resourcepath.end_with?('.xml')
71541a
     begin
71541a
       metadata = IO.read(resourcepath)
71541a
@@ -316,12 +329,16 @@ def getResourceMetadata(resourcepath)
71541a
     end
71541a
   else
71541a
     ENV['OCF_ROOT'] = OCF_ROOT
71541a
-    metadata = `#{resourcepath} meta-data`
71541a
+    stdout, stderr, retval = run_cmd(session, resourcepath, 'meta-data')
71541a
+    metadata = stdout.join
71541a
   end
71541a
 
71541a
   begin
71541a
     doc = REXML::Document.new(metadata)
71541a
-  rescue REXML::ParseException
71541a
+  rescue REXML::ParseException => e
71541a
+    $logger.error(
71541a
+      "Unable to parse metadata of resource agent '#{resourcepath}': #{e}"
71541a
+    )
71541a
     return [options_required, options_optional, [short_desc, long_desc]]
71541a
   end
71541a
 
71541a
@@ -381,7 +398,9 @@ def getResourceAgents(session, resource_agent=nil)
71541a
     if resource_agent and (a.start_with?("ocf:heartbeat:") or a.start_with?("ocf:pacemaker:"))
71541a
       split_agent = ra.name.split(/:/)
71541a
       path = OCF_ROOT + '/resource.d/' + split_agent[1] + "/" + split_agent[2]
71541a
-      required_options, optional_options, resource_info = getResourceMetadata(path)
71541a
+      required_options, optional_options, resource_info = getResourceMetadata(
71541a
+        session, path
71541a
+      )
71541a
       ra.required_options = required_options
71541a
       ra.optional_options = optional_options
71541a
       ra.info = resource_info
71541a
-- 
71541a
1.9.1
71541a